Android OpenGL 为整个网格渲染纹理的第一个像素

Android OpenGL rendering first pixel of a texture for the entire mesh

我正在尝试在正方形上渲染纹理。该正方形由 2 个三角形构成,并使用索引缓冲区。我遇到的问题是整个正方形是单一颜色,即使对于 S 和 T 坐标,我指定了整个纹理(其中有不同的颜色。)

我的贴图

如您所见,这是结果...

显示

这就是我想要做的... 我的着色器非常简单,只是一个简单的模型视图矩阵和一个位置

顶点着色器

uniform mat4 uMVPMatrix;

attribute vec4 aPosition;

attribute vec2 aTextureCoordinates;
varying vec2 vTextureCoordinates;

void main()
{
    vTextureCoordinates = aTextureCoordinates;
    gl_Position = uMVPMatrix * aPosition;
}

片段着色器

precision mediump float;

uniform sampler2D uTextureUnit;
varying vec2 vTextureCoordinates;

void main()
{
    gl_FragColor = texture2D(uTextureUnit, vTextureCoordinates);
}

GL 表面视图

在我的 GlSurfaceView 实例中,我获取了这些 GPU 变量并创建了一个新的 SquareTexture2 对象

@Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig)
    {
        int mvpHandle      = -1;
        int positionHandle = -1;
        int textureHandle  = -1;
        int textureUniform = -1;

        shader = new Shader(context, R.raw.vertex_texture, R.raw.fragment_texture);

        mvpHandle = GLES20.glGetUniformLocation(shader.getProgramID(),"uMVPMatrix");
        positionHandle  = GLES20.glGetAttribLocation(shader.getProgramID(), "aPosition");
        textureHandle = GLES20.glGetAttribLocation(shader.getProgramID(),"aTextureCoordinate");
        textureUniform = GLES20.glGetUniformLocation(shader.getProgramID(),"uTextureUnits");

        camera = new Camera(mvpHandle);
        squareTexture = new SquareTexture2(positionHandle,textureUniform,textureHandle, this.context, R.raw.texture1);
        GLES20.glClearColor(0.0f, 0.5f, 0.0f, 1.0f);
    }

SquareTexture2

我的 SquareTexture2 class 真的很简单...只有 2 个方法。构造函数和渲染函数。

 public SquareTexture2(int aPositionHandle, int uTextureHandle, int aTextureHandle, Context context, int resourceId)
    {
        float[] shape =
        {
                1.0f,  1.0f,  1.0f, 1.0f,
               -1.0f,  1.0f,  0.0f, 1.0f,
               -1.0f, -1.0f,  0.0f, 0.0f,
                1.0f, -1.0f,  1.0f, 0.0f,
        };
        int[] index =
        {
                0, 2, 1,
                0, 3, 2,
        };

        this.aPositionHandle = aPositionHandle;
        this.uTextureHandle = uTextureHandle;
        this.aTextureHandle = aTextureHandle;

        vertexObject = new VertexObject(shape,index);

        texture = new Texture(context, resourceId);
        this.textureId = texture.getTextureId();
    }

    public void render()
    {
        GLES20.glEnableVertexAttribArray(aPositionHandle);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,this.textureId);
        GLES20.glUniform1i(this.uTextureHandle, 0);

        vertexObject.bind(aPositionHandle, aTextureHandle,16);
        vertexObject.render();
    }

最后是 Texture class 定义和 VertexBuffer class 定义,这也非常简单。

顶点对象

public class VertexObject
{
    private FloatBuffer vertexData;
    private IntBuffer   indexData;

    private int vertexBufferId;
    private int indexBufferId;

    public VertexObject(float[] shape, int[] index)
    {
        this.createVertexData(shape);
        this.createIndexData(index);
    }

    private void createVertexData(float[] shape)
    {
        ByteBuffer vbb = ByteBuffer.allocateDirect(shape.length*4);
        vbb.order(ByteOrder.nativeOrder());

        vertexData = vbb.asFloatBuffer();
        vertexData.put(shape);
        vertexData.position(0);

        int[] buffer = new int[1];
        GLES20.glGenBuffers(1,buffer,0);

        if(buffer[0] == 0)
        {
            throw new RuntimeException("Unable to generate Buffer");
        }
        this.vertexBufferId = buffer[0];

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER,vertexData.capacity()*4, vertexData,GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    }

    private void createIndexData(int[] index)
    {
        ByteBuffer ibb = ByteBuffer.allocateDirect(index.length*4);
        ibb.order(ByteOrder.nativeOrder());

        indexData = ibb.asIntBuffer();
        indexData.put(index);
        indexData.position(0);

        int[] buffer = new int[1];
        GLES20.glGenBuffers(1,buffer,0);

        if(buffer[0] == 0)
        {
            throw new RuntimeException("Unable to generate Buffer");
        }
        this.indexBufferId = buffer[0];

        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
        GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexData.capacity()*4, indexData, GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,0);
    }



    public void bind(int aPosition, int aTextureCoordinate, int stride)
    {
        vertexData.position(0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,this.vertexBufferId);
        GLES20.glVertexAttribPointer(aPosition, 2, GLES20.GL_FLOAT,false, stride, 0);

        GLES20.glEnableVertexAttribArray(aPosition);

        vertexData.position(2);
        GLES20.glVertexAttribPointer(aTextureCoordinate,2,GLES20.GL_FLOAT,false,stride,vertexData);
        GLES20.glEnableVertexAttribArray(aTextureCoordinate);
    }

    public void render()
    {
        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, this.indexBufferId);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_INT,0);

        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER,0);
    }

}

纹理

public class Texture
{
    private int textureId;

    public Texture(Context context, int resourceId)
    {
        int[] textureObjectIds = new int[1];

        GLES20.glGenTextures(1, textureObjectIds, 0);

        if (textureObjectIds[0] == 0)
        {
            throw new RuntimeException("Unable to create a texture");
        }
        this.textureId = textureObjectIds[0];

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;

        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);

        if (bitmap == null)
        {
            throw new RuntimeException("Unable to decode a bitmap");
        }

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureObjectIds[0]);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);


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

        bitmap.recycle();

        GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    }

    public int getTextureId()
    {
        return this.textureId;
    }

}

我正在编写的“OpenGL ES 2 for Android”这本书没有纹理和索引缓冲区,这似乎是问题所在。我也看过这个视频一百万次,但仍然无法正常工作。 https://www.youtube.com/watch?v=n4k7ANAFsIQ&ab_channel=TheCherno

当命名缓冲区对象绑定到目标 GL_ARRAY_BUFFER 时,glVertexAttribPointer 的最后一个参数被视为该缓冲区中的字节偏移量。
指定纹理坐标时,glVertexAttribPointer 的最后一个参数必须是 8(字节)而不是缓冲区:

GLES20.glVertexAttribPointer(aTextureCoordinate,2,GLES20.GL_FLOAT,false,stride,vertexData);

GLES20.glVertexAttribPointer(aTextureCoordinate,2,GLES20.GL_FLOAT,false,stride, 8);

指定顶点坐标时,偏移量为0,您做对了。