OpenGL ES纹理无法正确渲染

时间:2019-02-06 16:29:13

标签: java android opengl-es texture-mapping

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

示例:

我的网状纹理

my meshe obj file

my texture png file

我找不到问题所在。

我的片段着色器

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);

现在,“我的网格”及其纹理已正确显示。

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

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

在下面的代码中,您将创建一个新的纹理坐标数组,该数组具有与顶点坐标数组一样多的项目。仅当每个顶点坐标与1个纹理坐标精确关联时,该方法才有效。该文件中有7536个纹理坐标和7366个顶点cooodinates。

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坐标。 参见Rendering meshes with multiple indices

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

让我们假设您有一个 .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());