我正在尝试将简单的2D纹理加载到正方形上并将其显示在我的GLSurfaceView背面。我复制并修复了Draw a 2D Image using OpenGL ES 2.0中的代码,但是当我安装并运行应用程序时,图像被分成4个象限,然后重新排列。
这是我实现的Sprite类:
public class Sprite {
//Reference to Activity Context
private final Context mActivityContext;
//Added for Textures
private final FloatBuffer mCubeTextureCoordinates;
private int mTextureUniformHandle;
private int mTextureCoordinateHandle;
private final int mTextureCoordinateDataSize = 2;
private int mTextureDataHandle;
private final String vertexShaderCode =
//Test
"attribute vec2 a_TexCoordinate;" +
"varying vec2 v_TexCoordinate;" +
//End Test
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
//Test
"v_TexCoordinate = a_TexCoordinate;" +
//End Test
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 v_Color;" +
//Test
"uniform sampler2D u_Texture;" +
"varying vec2 v_TexCoordinate;" +
//End Test
"void main() {" +
//"gl_FragColor = v_Color;" +
//"gl_FragColor = (v_Color * texture2D(u_Texture, v_TexCoordinate));" +
// Just draw the texture, don't apply a color
"gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" +
"}";
private final int shaderProgram;
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 2;
static float spriteCoords[] = {
-0.5f, 0.5f, // top left
-0.5f, -0.5f, // bottom left
0.5f, -0.5f, // bottom right
0.5f, 0.5f //top right
};
private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; //Order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; //Bytes per vertex
// Set color with red, green, blue and alpha (opacity) values
float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
// Image to draw as a texture
final int textureID = R.raw.quadrants;
public Sprite(final Context activityContext) {
mActivityContext = activityContext;
//Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
ByteBuffer bb = ByteBuffer.allocateDirect(spriteCoords.length * 4);
//Use the Device's Native Byte Order
bb.order(ByteOrder.nativeOrder());
//Create a floating point buffer from the ByteBuffer
vertexBuffer = bb.asFloatBuffer();
//Add the coordinates to the FloatBuffer
vertexBuffer.put(spriteCoords);
//Set the Buffer to Read the first coordinate
vertexBuffer.position(0);
// S, T (or X, Y)
// Texture coordinate data.
// Because images have a Y axis pointing downward (values increase as you move down the image) while
// OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis.
// What's more is that the texture coordinates are the same for every face.
final float[] cubeTextureCoordinateData =
{
//Front face
/*0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f*/
/*
This was in the code in the aforementioned StackOverflow post,
but doesn't work either
-0.5f, 0.5f,
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f
*/
0.5f, 0.5f,
0.5f, -0.5f,
-0.5f, -0.5f,
-0.5f, 0.5f
};
mCubeTextureCoordinates = ByteBuffer
.allocateDirect(cubeTextureCoordinateData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mCubeTextureCoordinates.put(cubeTextureCoordinateData).position(0);
//Initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(spriteCoords.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
shaderProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(shaderProgram, vertexShader);
GLES20.glAttachShader(shaderProgram, fragmentShader);
//Texture Code
GLES20.glBindAttribLocation(shaderProgram, 0, "a_TexCoordinate");
GLES20.glLinkProgram(shaderProgram);
//Load the texture
mTextureDataHandle = loadTexture(mActivityContext, textureID);
}
public void draw(float[] mvpMatrix) {
//Add program to OpenGL ES Environment
GLES20.glUseProgram(shaderProgram);
//Get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
//Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
//Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
//Get Handle to Fragment Shader's v_Color member
mColorHandle = GLES20.glGetUniformLocation(shaderProgram, "v_Color");
//Set the Color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//Set Texture Handles and bind Texture
mTextureUniformHandle = GLES20.glGetAttribLocation(shaderProgram, "u_Texture");
mTextureCoordinateHandle = GLES20.glGetAttribLocation(shaderProgram, "a_TexCoordinate");
//Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
//Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(mTextureUniformHandle, 0);
//Pass in the texture coordinate information
mCubeTextureCoordinates.position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 0, mCubeTextureCoordinates);
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
//Get Handle to Shape's Transformation Matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
//Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
//Draw the triangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
//Disable Vertex Array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public static int loadTexture(final Context context, final int resourceId)
{
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false; // No pre-scaling
// Read in the resource
final Bitmap bitmap = BitmapFactory
.decodeResource(context.getResources(), resourceId, options);
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_NEAREST);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
}
if (textureHandle[0] == 0)
{
throw new RuntimeException("Error loading texture.");
}
return textureHandle[0];
}
}
例如,http://changesuk.net/wp-content/uploads/2009/05/4-quadrants.jpg处的图像被更改,以便数字都正确定向,但是处于模式中
4 | 3
-----
2 | 1
如果我改变添加正方形坐标的顺序(例如从“左下角”开始),图像的象限会移动,但也围绕各自的中心旋转(在上图的情况下) ,数字都将在他们的身边或头上)。我查看了每一行代码,无法理解这里发生了什么。有没有人以前遇到过这种行为,或者有人可以解释可能导致这种行为的原因?
答案 0 :(得分:2)
我已经看了一段时间了,但我从来没有机会回答问题。我知道这个问题已经有5个月了,但无论如何我都会尝试回答它。
基本上,你正在做的是你没有正确地将纹理的角与顶点的角对齐。你得到了
4 | 3
-----
2 | 1
因为纹理的中心位于正方形的右下方,纹理会重复出现,因此4和2位于右侧,3位于顶部。
解决此问题的方法是在顶点着色器中添加新的vec4
uniform vec4 pos;
并用x,y,宽度和高度填充
int pos = GLES20.glGetUniformLocation(mProgram, "pos");
GLES20.glUniform4f(pos, .5f, .5f, 1, 1); // x, y, width, height
现在改变这个
v_TexCoordinate = a_TexCoordinate;
到这个
v_TexCoordinate = vec2((a_TexCoordinate.x - pos.x)/pos.z, (a_TexCoordinate.y - pos.y)/pos.w);
这应该可以解决问题。
答案 1 :(得分:0)
我尝试过P0rter解决方案,但它对我没用。
要明确的是,问题不在于象限的排列,而是在中心开始。其他方面只是默认重复。 如果你打开它,当加载纹理时,它将只绘制右下象限。
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE
);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE
);
我有另一个解决方案。我不知道我在做什么,所以我只是粘贴,对我有用。
public class Sprite extends GLObject {
final static float COORDS_SQUARE[] = {
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f
};
/**
* Number of coordinates per point in this array.
*/
protected static final int COORDINATES_PER_VERTEX = 3;
final static float TEXTURE_COORDS[] = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
/**
* Number of coordinates per point in this array.
*/
protected static final int COORDINATES_PER_TEXTURE_VERTEX = 2;
/**
* Order to draw COORDINATES_VERTICES
*/
protected static final short DRAW_ORDER[] = {
0, 1, 2, 0, 3, 2
};
private static final String VERTEX_SHADER_CODE =
"attribute vec2 aTexCoordinate;" +
"varying vec2 vTexCoordinate;" +
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
" vTexCoordinate = aTexCoordinate;" +
"}";
private static final String FRAGMENT_SHADER_CODE =
"precision mediump float;" +
"uniform sampler2D uTexture;" +
"varying vec2 vTexCoordinate;" +
"void main() {" +
" gl_FragColor = texture2D(uTexture, vTexCoordinate);" +
"}";
private final int mProgram;
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final FloatBuffer mTextureCoordinates;
private int mTextureDataHandle;
public Sprite(final Context context, @DrawableRes int resID) {
//Initialize Vertex Byte Buffer for Shape Coordinates / # of coordinate values * 4 bytes per float
ByteBuffer vb = ByteBuffer.allocateDirect(COORDS_SQUARE.length * 4);
//Use the Device's Native Byte Order
vb.order(ByteOrder.nativeOrder());
//Create a floating point buffer from the ByteBuffer
vertexBuffer = vb.asFloatBuffer();
//Add the coordinates to the FloatBuffer
vertexBuffer.put(COORDS_SQUARE);
//Set the Buffer to Read the first coordinate
vertexBuffer.position(0);
ByteBuffer tcb = ByteBuffer.allocateDirect(TEXTURE_COORDS.length * 4);
tcb.order(ByteOrder.nativeOrder());
mTextureCoordinates = tcb.asFloatBuffer();
mTextureCoordinates.put(TEXTURE_COORDS);
mTextureCoordinates.position(0);
//Initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(DRAW_ORDER.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(DRAW_ORDER);
drawListBuffer.position(0);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
//Texture Code
GLES20.glBindAttribLocation(mProgram, 0, "aTexCoordinate");
GLES20.glLinkProgram(mProgram);
//Load the texture
mTextureDataHandle = loadTexture(context, resID);
}
public void draw(float[] mvpMatrix) {
//Add program to OpenGL ES Environment
GLES20.glUseProgram(mProgram);
//Get handle to vertex shader's vPosition member
int positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
int textureUniformHandle = GLES20.glGetAttribLocation(mProgram, "uTexture");
int textureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoordinate");
int MVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//Enable a handle to the triangle COORDINATES_VERTICES
GLES20.glEnableVertexAttribArray(positionHandle);
//Prepare the triangle coordinate data
vertexBuffer.position(0);
GLES20.glVertexAttribPointer(
positionHandle, COORDINATES_PER_VERTEX,
GLES20.GL_FLOAT, false,
0, vertexBuffer
);
//Set the active texture unit to texture unit 0.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//Bind the texture to this unit.
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);
//Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
GLES20.glUniform1i(textureUniformHandle, 0);
//Pass in the texture coordinate information
mTextureCoordinates.position(0);
GLES20.glVertexAttribPointer(
textureCoordinateHandle, COORDINATES_PER_TEXTURE_VERTEX,
GLES20.GL_FLOAT, false,
0, mTextureCoordinates
);
GLES20.glEnableVertexAttribArray(textureCoordinateHandle);
//Apply the projection and view transformation
GLES20.glUniformMatrix4fv(MVPMatrixHandle, 1, false, mvpMatrix, 0);
//Draw the triangle
drawListBuffer.position(0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, DRAW_ORDER.length, GLES20.GL_UNSIGNED_SHORT,
drawListBuffer);
//Disable Vertex Array
GLES20.glDisableVertexAttribArray(positionHandle);
}
}
在我的GLObject类中有一些用于加载纹理等的共享方法,没什么重要的。