首页 > 解决方案 > OpenGL ES 纹理未正确渲染

问题描述

我正在使用 openglES 3.0 开发一个 Android 应用程序,我想创建一个 obj 加载器系统并显示一个带有他的纹理的 3D 模型。我的代码正确显示没有纹理的 3D 网格。如果我尝试添加纹理,它将显示纹理,并且纹理的某些部分将是空三角形。

例子 :

我的网格有纹理

我的网格 obj 文件

我的纹理 png 文件

我找不到什么问题。

我的片段着色器

precision mediump float;
uniform vec4 vColor;
uniform sampler2D uTexture;
varying vec2 oTexCoordinate;

void main() {
    gl_FragColor = texture2D(uTexture, oTexCoordinate);
    //gl_FragColor = vec4(1, 0.5, 0, 1.0);

}

我的顶点着色器

attribute vec4 position;
uniform mat4 matrix;

attribute vec2 vTexCoordinate;
varying vec2 oTexCoordinate;


void main() {
    oTexCoordinate = vTexCoordinate;
    gl_Position = matrix * position;

}

提前致谢。


更新 :

谢谢。

我已经更改了我的代码以符合您的想法,我已经激活了面部剔除并为正常添加了一个缓冲区,我现在不使用它。我把我的新代码放在下面。但问题并没有完全解决,见下图。

我想我需要将正常信息传递给着色器,但我不确定如何正确执行它或者它是否是解决方案。

public class MeshLoader {

    private int program;

    private List<String> facesVertexList;
    private List<String> facesTextureList;
    private List<String> facesNormalList;

    private List<String> verticesList;
    private List<String> textureList;
    private List<String> normalList;

    private FloatBuffer verticesBuffer;
    private FloatBuffer verticesBufferTemp;

    private FloatBuffer facesVertexBuffer;
    private FloatBuffer facesTextureBuffer;
    private FloatBuffer facesNormalBuffer;

    private FloatBuffer textureBuffer;
    private FloatBuffer textureBufferTemp;

    private FloatBuffer normalBuffer;
    private FloatBuffer normalBufferTemp;

    private Context contextMeshLoader;
    final int[] textureHandle = new int[1];


    public MeshLoader(Context context) {
        contextMeshLoader = context;
        textureList = new LinkedList<>();
        verticesList = new LinkedList<>();
        normalList = new LinkedList<>();
        facesVertexList = new LinkedList<>();
        facesTextureList = new LinkedList<>();
        facesNormalList = new LinkedList<>();

        openObjFile(0);

        String vertexShaderCode = "";
        try{
            InputStream vertexShaderStream = context.getResources().openRawResource(R.raw.vertex_shader);
            vertexShaderCode = IOUtils.toString(vertexShaderStream, Charset.defaultCharset());
            vertexShaderStream.close();
        }
        catch (Exception e){
            Log.e("MeshReaderActivity", "Error reading vertex shader", e);
        }

        String fragmentShaderCode = "";
        try{
            InputStream fragmentShaderStream = context.getResources().openRawResource(R.raw.fragment_shader);
            fragmentShaderCode = IOUtils.toString(fragmentShaderStream, Charset.defaultCharset());
            fragmentShaderStream.close();
        }
        catch(Exception e){
            Log.e("MeshReaderActivity", "Error reading fragment shader", e);
        }

        int vertexShader = GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);
        GLES30.glShaderSource(vertexShader, vertexShaderCode);

        int fragmentShader = GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);
        GLES30.glShaderSource(fragmentShader, fragmentShaderCode);


        GLES30.glCompileShader(vertexShader);
        GLES30.glCompileShader(fragmentShader);


        program = GLES30.glCreateProgram();
        GLES30.glAttachShader(program, vertexShader);
        GLES30.glAttachShader(program, fragmentShader);
        GLES30.glLinkProgram(program);
        GLES30.glUseProgram(program);

    }

    public void openObjFile(int value)
    {
        InputStream is;
        value = 0;
        if(value == 0)
           is  = contextMeshLoader.getResources().openRawResource(R.raw.objface);
        else
            is = contextMeshLoader.getResources().openRawResource(R.raw.objship);

        if(verticesBufferTemp != null)
            verticesBufferTemp.clear();
        if(facesVertexBuffer != null)
            facesVertexBuffer.clear();
        if(textureBuffer != null)
            textureBuffer.clear();
        if(verticesList != null)
            verticesList.clear();
        if(facesVertexList != null)
            facesVertexList.clear();
        if(textureList != null)
            textureList.clear();

        try{
            byte[] buffer = new byte[is.available()];
            is.read(buffer);
            String data = new String(buffer);

            parseData(data);



            ByteBuffer buffer2 = ByteBuffer.allocateDirect(facesVertexList.size() * 3 * 4);
            buffer2.order(ByteOrder.nativeOrder());
            facesVertexBuffer = buffer2.asFloatBuffer();

            ByteBuffer buffer3 = ByteBuffer.allocateDirect(facesTextureList.size() * 3 * 4);
            buffer3.order(ByteOrder.nativeOrder());
            facesTextureBuffer = buffer3.asFloatBuffer();

            ByteBuffer buffer6 = ByteBuffer.allocateDirect(facesTextureList.size() * 3 * 4);
            buffer6.order(ByteOrder.nativeOrder());
            facesNormalBuffer = buffer6.asFloatBuffer();


            for(String face: facesVertexList) {
                String vertexIndices[] = face.split("\\s+");
                float vertex1 = Float.parseFloat(vertexIndices[1]);
                float vertex2 = Float.parseFloat(vertexIndices[2]);
                float vertex3 = Float.parseFloat(vertexIndices[3]);
                facesVertexBuffer.put((vertex1 - 1));
                facesVertexBuffer.put((vertex2 - 1));
                facesVertexBuffer.put((vertex3 - 1));
            }
            facesVertexBuffer.position(0);


            for(String texture: facesTextureList){
                String textureIndice[] = texture.split("\\s+");
                float texture1 = Float.parseFloat(textureIndice[1]);
                float texture2 = Float.parseFloat(textureIndice[2]);
                float texture3 = Float.parseFloat(textureIndice[3]);
                facesTextureBuffer.put((texture1 - 1));
                facesTextureBuffer.put((texture2 - 1));
                facesTextureBuffer.put((texture3 - 1));
            }
            facesTextureBuffer.position(0);

            for(String normal: facesNormalList) {
                String normalIndice[] = normal.split("\\s+");
                float normal1 = Float.parseFloat(normalIndice[1]);
                float normal2 = Float.parseFloat(normalIndice[2]);
                float normal3 = Float.parseFloat(normalIndice[3]);
                facesNormalBuffer.put((normal1 - 1));
                facesNormalBuffer.put((normal2 - 1));
                facesNormalBuffer.put((normal3 - 1));
            }
            facesNormalBuffer.position(0);


            ByteBuffer buffer1 = ByteBuffer.allocateDirect(verticesList.size() * 3 * 4);
            buffer1.order(ByteOrder.nativeOrder());
            verticesBufferTemp = buffer1.asFloatBuffer();

            ByteBuffer buffer5 = ByteBuffer.allocateDirect(textureList.size() * 2 * 4);
            buffer5.order(ByteOrder.nativeOrder());
            textureBufferTemp = buffer5.asFloatBuffer();

            ByteBuffer buffer7 = ByteBuffer.allocateDirect(textureList.size() * 3 * 4);
            buffer7.order(ByteOrder.nativeOrder());
            normalBufferTemp = buffer7.asFloatBuffer();

            for(String vertex: verticesList) {
                String coords[] = vertex.split("\\s+");
                float x = Float.parseFloat(coords[1]);
                float y = Float.parseFloat(coords[2]);
                float z = Float.parseFloat(coords[3]);
                verticesBufferTemp.put(x);
                verticesBufferTemp.put(y);
                verticesBufferTemp.put(z);
            }
            verticesBufferTemp.position(0);

            for (String texture:textureList)
            {
                String textureIndices[] = texture.split("\\s+");
                float texture1 = Float.parseFloat(textureIndices[1]);
                float texture2 = Float.parseFloat(textureIndices[2]);
                textureBufferTemp.put(texture1);
                textureBufferTemp.put(texture2);
            }
            textureBufferTemp.position(0);

            for (String normal:normalList)
            {
                String normalIndices[] = normal.split("\\s+");
                float normal1 = Float.parseFloat(normalIndices[1]);
                float normal2 = Float.parseFloat(normalIndices[2]);
                normalBufferTemp.put(normal1);
                normalBufferTemp.put(normal2);
            }
            normalBufferTemp.position(0);


            System.out.println("size remaining " + facesVertexBuffer.remaining());

            ByteBuffer bufferV = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 3 * 4);
            bufferV.order(ByteOrder.nativeOrder());
            verticesBuffer = bufferV.asFloatBuffer();

            ByteBuffer bufferT = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 2 * 4);
            bufferT.order(ByteOrder.nativeOrder());
            textureBuffer = bufferT.asFloatBuffer();

            ByteBuffer bufferN = ByteBuffer.allocateDirect(facesVertexBuffer.remaining() * 3 * 4);
            bufferN.order(ByteOrder.nativeOrder());
            normalBuffer = bufferN.asFloatBuffer();

            int size = facesVertexBuffer.remaining();

            for(int i = 0; i < size;i++)
            {
                int faceVertex = Math.round(facesVertexBuffer.get(i)) ;
                int faceTexture = Math.round(facesTextureBuffer.get(i));
                int faceNormal = Math.round(facesNormalBuffer.get(i));

                float x = verticesBufferTemp.get((faceVertex)*3);
                float y = verticesBufferTemp.get(((faceVertex)*3)+1);
                float z = verticesBufferTemp.get(((faceVertex)*3)+2);
                verticesBuffer.put( i*3,   x);
                verticesBuffer.put( (i*3)+1, y);
                verticesBuffer.put( (i*3)+2, z);

                float u = textureBufferTemp.get((faceTexture)*2);
                float v = -textureBufferTemp.get(((faceTexture)*2)+1);
                textureBuffer.put( i*2,   u);
                textureBuffer.put( (i*2)+1, v);

                float xn = normalBufferTemp.get((faceNormal*3));
                float yn = normalBufferTemp.get((faceNormal*3)+1);
                float zn = normalBufferTemp.get((faceNormal*3)+2);
                normalBuffer.put(i*3,xn);
                normalBuffer.put((i*3)+1,yn);
                normalBuffer.put((i*3)+2,zn);

            }
            verticesBuffer.position(0);
            textureBuffer.position(0);
            normalBuffer.position(0);

            is.close();
            loadTexture();
        }
        catch (Exception e) {
            Log.e("MeshReaderActivity", "Error reading objfile", e);
        }
    }

    public void parseData(String dataToParse)
    {
        Log.i("parse data method", "parse data method");
        String[] data = dataToParse.split("\n");
        for (int i = 0;i < data.length;i++)
        {
            String line = data[i];
            if(line.startsWith("v "))
            {
                // Add vertex line to list of vertices
                verticesList.add(line);
            }
            else if(line.startsWith("vt "))
            {
                textureList.add(line);
            }
            else if(line.startsWith("vn "))
            {
                normalList.add(line);
            }
            else if(line.startsWith("f "))
            {
                // Add face line to faces list
                triangulate(line);
            }
        }

    }

    public void triangulate(String lineToTriangulate)
    {
        String lineSplit[] = lineToTriangulate.split("\\s+");
        if(lineSplit.length > 4)
        {
            String line1="";
            String line2="";
            if (lineToTriangulate.contains("/"))
            {
                line1 = lineSplit[0] + " " + lineSplit[1].split("/")[0] + " " + lineSplit[2].split("/")[0] + " " + lineSplit[3].split("/")[0];
                line2 = lineSplit[0] + " " + lineSplit[1].split("/")[0] + " " + lineSplit[2].split("/")[0] + " " + lineSplit[4].split("/")[0];
            }
            else
            {
                line1 = lineSplit[0] + " " + lineSplit[1] + " " + lineSplit[2] + " " + lineSplit[3];
                line2 = lineSplit[0] + " " + lineSplit[1] + " " + lineSplit[2] + " " + lineSplit[4];
            }
            facesVertexList.add(line1);
            facesVertexList.add(line2);
        }
        else
        {
            if(lineToTriangulate.contains("/"))
            {
                String[] splitElement1 = lineSplit[1].split("/");
                String[] splitElement2 = lineSplit[2].split("/");
                String[] splitElement3 = lineSplit[3].split("/");
                String line = lineSplit[0] + " " + splitElement1[0] + " " + splitElement2[0] + " " + splitElement3[0];
                facesVertexList.add(line);
                line = lineSplit[0] + " " + splitElement1[1] + " " + splitElement2[1] + " " + splitElement3[1];
                facesTextureList.add(line);
                line =  lineSplit[0] + " " + splitElement1[2] + " " + splitElement2[2] + " " + splitElement3[2];
                facesNormalList.add(line);

            }
            else
            {
                facesVertexList.add(lineToTriangulate);
            }
        }
    }


    public void draw(float scratch[],float zoom){


        int position = GLES30.glGetAttribLocation(program, "position");
        GLES30.glEnableVertexAttribArray(position);
        GLES30.glVertexAttribPointer(position, 3, GLES30.GL_FLOAT, false, 3 * 4, verticesBuffer);


        int mTextureUniformHandle = GLES30.glGetUniformLocation(program, "uTexture");
        int mTextureCoordinateHandle = GLES30.glGetAttribLocation(program, "vTexCoordinate");

        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glUniform1i(mTextureUniformHandle, 0);
        GLES30.glEnableVertexAttribArray(mTextureCoordinateHandle);
        GLES30.glVertexAttribPointer(mTextureCoordinateHandle, 2, GLES30.GL_FLOAT, false, 2*4, textureBuffer);

        int normalHandle = GLES30.glGetAttribLocation(program,"normal");
        GLES30.glEnableVertexAttribArray(normalHandle);
        GLES30.glVertexAttribPointer(normalHandle,3,GLES30.GL_FLOAT,false,3*4,normalBuffer);

        float[] projectionMatrix = new float[16];
        float[] viewMatrix = new float[16];
        float[] productMatrix = new float[16];

        Matrix.frustumM(projectionMatrix, 0,
                -1, 1,
                -1, 1,
                1, 11);
        Matrix.setLookAtM(viewMatrix, 0,
                0, 0, zoom,
                0, 0, 0,
                0, 1, 0);
        Matrix.multiplyMM(productMatrix, 0,
                projectionMatrix, 0,
                viewMatrix, 0);

        float[] finalMatrix = new float[16];

        Matrix.multiplyMM(finalMatrix, 0,
                productMatrix, 0,
                scratch, 0);

        Matrix.rotateM(finalMatrix, 0, 180, 0.0f, 1.0f, 0.0f);


        int matrix = GLES30.glGetUniformLocation(program, "matrix");
        GLES30.glUniform1i(matrix,0);
        //GLES30.glUniformMatrix4fv(matrix, 1, false, productMatrix, 0);
        GLES30.glUniformMatrix4fv(matrix, 1, false, finalMatrix, 0);


        int size = facesVertexBuffer.remaining();

        GLES30.glEnable(GLES30.GL_CULL_FACE);
        GLES30.glCullFace(GLES30.GL_BACK);


        GLES30.glDrawArrays(GLES30.GL_TRIANGLES,0,size);

        GLES30.glDisableVertexAttribArray(position);
        GLES30.glDisableVertexAttribArray(mTextureCoordinateHandle);
    }

    public void loadTexture()
    {

        GLES30.glGenTextures(1, textureHandle,0);

        if (textureHandle[0] == 0)
        {
            throw new RuntimeException("Error generating texture name.");
        }


        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = true;    // No pre-scaling

        Bitmap bitmap = BitmapFactory.decodeResource(contextMeshLoader.getResources(), R.raw.pngface, options);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureHandle[0]);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);


        GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);

        bitmap.recycle();
    }

}

再次感谢。


更新

对于浮动,这是复制/粘贴错误。对于我的显示问题,我找到了解决方案。我只需要添加 onDrawFrame 方法。

GLES30.glEnable(GLES30.GL_DEPTH_TEST);

现在,我的网格和他的纹理已正确显示。

谢谢你的帮助。

标签: javaandroidopengl-estexture-mapping

解决方案


你的假设是错误的。相同的顶点坐标可以关联到不同的纹理坐标。

在下面的代码中,您将创建一个新的纹理坐标数组,其中包含与顶点坐标数组一样多的项目。仅当每个顶点坐标都与恰好 1 个纹理坐标相关联时,这才有效。文件中有 7536 个纹理坐标和 7366 个顶点坐标。

public void parseTexture()
{
    int size = facesVertexBuffer.remaining();
    System.out.println("size " + size);
    for(int i = 0; i < size;i++)
    {
        int faceVertex = facesVertexBuffer.get(i);
        int faceTexture = facesTextureBuffer.get(i);
        float a = textureBufferTemp.get((faceTexture)*2);
        float b = -textureBufferTemp.get(((faceTexture)*2)+1);
        textureBuffer.put((faceVertex*2),a);
        textureBuffer.put(((faceVertex)*2)+1,b);
   }
   textureBuffer.position(0);

   System.out.println("end parse texture");
}

如果顶点坐标和纹理坐标有不同的索引,则必须“复制”顶点位置。顶点坐标及其属性(如纹理坐标)形成数据记录。您可以将 3D 顶点坐标和 2D 纹理坐标想象为单个 5D 坐标。请参阅使用多个索引渲染网格

每个顶点位置的顶点属性形成一组数据。这意味着您必须创建顶点坐标和纹理坐标的元组。

假设您有一个这样的.obj文件:

v -1 -1 -1
v  1 -1 -1
v -1  1 -1
v  1  1 -1
v -1 -1  1
v  1 -1  1
v -1  1  1
v  1  1  1

vt 0 0
vt 0 1
vt 1 0
vt 1 1

vn -1  0  0
vn  0 -1  0
vn  0  0 -1
vn  1  0  0
vn  0  1  0
vn  0  0  1

f 3/1/1 1/2/1 5/4/1 7/3/1
f 1/1/2 2/2/2 3/4/2 6/3/2
f 3/1/3 4/2/3 2/4/3 1/3/3
f 2/1/4 4/2/4 8/4/4 6/3/4
f 4/1/5 3/2/5 7/4/5 8/3/5
f 5/1/6 6/2/6 8/4/6 7/3/6

从这里你必须找到顶点坐标、纹理纹理坐标和法线向量索引的所有组合,它们在面规范中使用:

 0 : 3/1/1
 1 : 1/2/1
 2 : 5/4/1
 3 : 7/3/1
 4 : 1/1/2
 5 : 2/2/2
 6 : 3/4/2
 7 : 6/3/2
 8 : ...

然后您必须创建与组合索引数组相对应的顶点坐标、纹理坐标和法线向量数组。顶点坐标及其属性既可以在一个数组中组合成数据集,也可以组合成三个具有相同数量属性的数组:

 index   vx vy vz     u v     nx ny nz
 0 :     -1  1 -1     0 0     -1  0  0
 1 :     -1 -1 -1     0 1     -1  0  0
 2 :     -1 -1  1     1 1     -1  0  0
 3 :     -1  1  1     1 0     -1  0  0
 4 :     -1 -1 -1     0 0      0 -1  0
 5 :      1 -1 -1     0 1      0 -1  0
 6 :     -1  1 -1     1 1      0 -1  0
 7 :      1 -1  1     1 0      0 -1  0
 8 : ...

进一步注意,short用于应用程序索引的数据类型的范围为 [-32768, 32767]。这对于这个模型来说已经足够大了,但是较大模型的索引数量会超过这个限制。


最简单的解决方法是创建一个三角形基元数组。完全跳过索引缓冲区并用于GLES30.glDrawArrays()绘制网格。

将临时缓冲区中的顶点坐标和纹理坐标变红。

ByteBuffer bufferVTemp = ByteBuffer.allocateDirect(verticesList.size() * 2 * 4);
bufferVTemp.order(ByteOrder.nativeOrder());
verticesBufferTemp = bufferVTemp.asFloatBuffer();

ByteBuffer bufferTTemp = ByteBuffer.allocateDirect(textureList.size() * 2 * 4);
bufferTTemp.order(ByteOrder.nativeOrder());
textureBufferTemp = bufferTTemp.asFloatBuffer();

for(String vertex: verticesList) {
    String coords[] = vertex.split(" "); // Split by space
    float x = Float.parseFloat(coords[1]);
    float y = Float.parseFloat(coords[2]);
    float z = Float.parseFloat(coords[3]);
    verticesBufferTemp.put(x);
    verticesBufferTemp.put(y);
    verticesBufferTemp.put(z);
}
verticesBufferTemp.position(0);

for (String texture: textureList)
{
    String textureIndices[] = texture.split("\\s+");
    float texture1 = Float.parseFloat(textureIndices[1]);
    float texture2 = Float.parseFloat(textureIndices[2]);
    textureBufferTemp.put(texture1);
    textureBufferTemp.put(texture2);
}
textureBufferTemp.position(0);

然后创建一个三角形数组

ByteBuffer bufferV = ByteBuffer.allocateDirect(facesVertexBuffer.size() * 3 * 4);
bufferV.order(ByteOrder.nativeOrder());
verticesBuffer = bufferV.asFloatBuffer();

ByteBuffer bufferT = ByteBuffer.allocateDirect(facesVertexBuffer.size() * 2 * 4);
bufferT.order(ByteOrder.nativeOrder());
textureBuffer = bufferT.asFloatBuffer();

int size = facesVertexBuffer.remaining();
System.out.println("size " + size);
for(int i = 0; i < size;i++)
{
    int faceVertex = facesVertexBuffer.get(i);
    int faceTexture = facesTextureBuffer.get(i);

    float x = verticesBufferTemp.get((faceVertex)*2);
    float y = verticesBufferTemp.get(((faceVertex)*2)+1);
    float z = verticesBufferTemp.get(((faceVertex)*2)+1);
    verticesBuffer.put( i*3,    x);
    verticesBuffer.put( i*3+1,  y);
    verticesBuffer.put( i*3+2), z);

    float u = textureBufferTemp.get((faceTexture)*2);
    float v = -textureBufferTemp.get(((faceTexture)*2)+1);
    textureBuffer.put( i*2,   u);
    textureBuffer.put( i*2+1, v);
}
verticesBuffer.position(0);
textureBuffer.position(0);

通过以下方式绘制网格GLES30.glDrawArrays()

GLES30.glDrawElements(GLES30.GL_TRIANGLES, 0, facesVertexBuffer.size());

推荐阅读