Android OpenGL рендеринг первого пикселя текстуры для всей сетки

#java #android #opengl-es

#java #Android #opengl-es

Вопрос:

Я пытаюсь отобразить текстуру на квадрате. Квадрат построен из 2 треугольников и использует индексный буфер. Проблема, с которой я сталкиваюсь, заключается в том, что весь квадрат имеет один цвет, хотя для координат S и T я указал всю текстуру (в которой разные цвета).)

моя текстура
моя текстура

Как вы можете видеть, это и есть результат…

Это отображение эмулятора Android
Дисплей

Вот что я пытаюсь сделать… Мои шейдеры очень просты, просто простая матрица представления модели и позиция

Вершинный шейдер

 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 действительно прост… всего 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();
    }
 

And finally the Texture class definition and VertexBuffer class definition which is also pretty straight forward.

VertexObject

 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 для Android», нет текстур и индексных буферов, что, по-видимому, является проблемой. Я также смотрел это видео миллион раз и до сих пор не могу заставить его работать. https://www.youtube.com/watch?v=n4k7ANAFsIQamp;ab_channel=TheCherno

Комментарии:

1. Для справки, я также изменил цвет первого пикселя текстуры, и это меняет цвет квадрата, который я рендерю, поэтому я знаю, что это тот первый пиксель.

2. Опечатка? Ваш вершинный шейдер ссылается на атрибут aTextureCoordinates , тогда как ваш вызов glGetAttribLocation указывает aTextureCoordinate . Успешно ли ваша шейдерная программа компилируется / связывается?

3. @G.M. Спасибо тебе! Это была половина проблемы вместе с приведенным ниже ответом. Спасибо за ваши острые глаза! Я не могу поверить, что пропустил этот один символ в конце строки.

Ответ №1:

Когда именованный объект буфера привязан к цели 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, что вы сделали правильно.

Комментарии:

1. Спасибо! Я этого не знал. Ваш ответ был очень полезным и познавательным. Теперь у меня это работает благодаря вашему решению вместе с комментарием от @G.M.