#java #android #opengl-es
#java #Android #opengl-es
Вопрос:
Я пытаюсь отобразить текстуру на квадрате. Квадрат построен из 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 действительно прост… всего 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.