glDrawElements() 只绘制前两个面
glDrawElements() only draws first two faces
已解决。问题解决: attribStride
设置不正确
我正在编写一个小型 Google Cardboard 应用程序,它可以加载和渲染任何 Wavefront .obj 文件及其链接 .mtl 文件中表面的 material 属性。为了进行渲染,我使用了 VBO 和 IBO。对于表面和闪电的 material 属性,我使用了 phong 着色。
我的问题是,glDrawElements()
只会渲染由 12 个三角形组成的立方体的前两个三角形。
- 构成立方体的所有三角形都是逆时针排列的。
- 解析.obj文件生成的顶点数组和索引数组数据有效
- VBO客户端FloatBuffer和IBO客户端ShortBuffer的数据和长度有效。
glBufferData()
设置的VBO和IBO长度有效
- 着色器已正确编译和附加。
glGetAttribLocation()
和 glGetUniformLocation()
为着色器设置的所有属性和统一位置均有效。
我已经在调试器和 logcat 中检查了这些点。
我意识到它有很多代码。但是我在任何地方都找不到问题或问题的答案。所以请帮助 :D 首先感谢 :)
此方法设置 VBO、IBO、着色器程序、着色器程序位置、纹理和着色器程序统一值:
private void initializeGL() {
Log.i(TAG, "initializeGL()");
// Set camera matrix.
Matrix.setLookAtM(camera, OFFSET, EYE_POINT_X, EYE_POINT_Y, EYE_POINT_Z, CENTER_OF_VIEW_X, CENTER_OF_VIEW_Y, CENTER_OF_VIEW_Z, UP_VECTOR_X, UP_VECTOR_Y, UP_VECTOR_Z);
// Initialize shader program.
shaderProgram.init();
shaderProgramHandle = shaderProgram.getProgramHandle();
// Initialize object information.
objectInformation.init();
vboHandle = objectInformation.getVbo().getVboHandle();
vboLength = objectInformation.getVbo().getVboLength();
iboHandle = objectInformation.getIbo().getIboHandle();
iboLength = objectInformation.getIbo().getIboLength();
uniforms = objectInformation.getUniformArray();
numOfUniforms = objectInformation.getNumOfUniforms();
Log.i(TAG, "initializeGL(): Number of uniforms = " + numOfUniforms);
textures = objectInformation.getTextureArray();
numOfTextures = objectInformation.getNumOfTextures();
Log.i(TAG, "initializeGL(): Number of textures = " + numOfTextures);
// Get vertex shader uniform locations.
modelViewProjectionParam = GLES20.glGetUniformLocation(shaderProgramHandle, "u_ModelViewProjection");
Log.i(TAG, "initializeGL(): modelViewProjectionParam = " + modelViewProjectionParam);
modelViewParam = GLES20.glGetUniformLocation(shaderProgramHandle, "u_ModelView");
Log.i(TAG, "initializeGL(): modelViewParam = " + modelViewParam);
normalMatrixParam = GLES20.glGetUniformLocation(shaderProgramHandle, "u_NormalMatrix");
Log.i(TAG, "initializeGL(): normalMatrixParam = " + normalMatrixParam);
// Get vertex shader attribute locations.
positionParam = GLES20.glGetAttribLocation(shaderProgramHandle, "a_Position");
Log.i(TAG, "initializeGL(): positionParam = " + positionParam);
if(boolTexCoords) {
attribStride += 4 * BYTES_PER_FLOAT;
texCoordParam = GLES20.glGetAttribLocation(shaderProgramHandle, "a_TexCoord");
Log.i(TAG, "initializeGL(): texCoordParam = " + texCoordParam);
}
normalParam = GLES20.glGetAttribLocation(shaderProgramHandle, "a_Normal");
Log.i(TAG, "initializeGL(): normalParam = " + normalParam);
// Get fragment shader phong uniform locations.
if(boolPhongLightning) {
lightPosParam = GLES20.glGetUniformLocation(shaderProgramHandle, "lightPosition");
Log.i(TAG, "initializeGL(): lightPosParam = " + lightPosParam);
IaParam = GLES20.glGetUniformLocation(shaderProgramHandle, "Ia");
Log.i(TAG, "initializeGL(): IaParam = " + IaParam);
IpParam = GLES20.glGetUniformLocation(shaderProgramHandle, "Ip");
Log.i(TAG, "initializeGL(): IpParam = " + IpParam);
}
// Set fragment shader material uniforms.
GLES20.glUseProgram(shaderProgramHandle);
for(int i = 0; i < numOfUniforms; i++) {
String unifName = uniforms[i].getUnifName();
location = GLES20.glGetUniformLocation(shaderProgramHandle, unifName);
checkGLError(TAG, "initializeGL(): " + unifName + " shader location");
vec = uniforms[i].getArray();
if(vec != null) {
GLES20.glUniform3fv(location, 1, vec, 0);
Log.i(TAG, "initializeGL(): " + unifName + "_Param = " + location);
} else {
float value = uniforms[i].getValue();
GLES20.glUniform1f(location, value);
Log.i(TAG, "initializeGL(): " + unifName + "_Param = " + location);
}
}
// Set fragment shader phong uniforms.
if(boolPhongLightning) {
GLES20.glUniform3fv(IaParam, 1, Ia, 0);
GLES20.glUniform3fv(IpParam, 1, Ip, 0);
}
// Set fragment shader textures.
for(int i = 0; i < numOfTextures; i++) {
// Set active texture unit.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
// Bind texture to active texture unit.
int texHandle = textures[i].getTextureHandle();
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texHandle);
// Assign texture unit to uniform in fragment shader.
String unifName = textures[i].getUnifName();
location = GLES20.glGetUniformLocation(shaderProgramHandle, unifName);
GLES20.glUniform1i(location, i);
checkGLError(TAG, "initializeGL(): glUniform1i()");
Log.i(TAG, "initializeGL(): " + unifName + "Param = " + location);
}
GLES20.glUseProgram(0);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glFrontFace(GLES20.GL_CCW);
GLES20.glCullFace(GLES20.GL_BACK);
GLES20.glClearColor(0.3f, 0.3f, 0.3f, 0.1f);
}
public void onDrawEye():
@Override
public void onDrawEye(Eye eye) {
Log.i(TAG, "onDrawEye()");
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
projection = eye.getPerspective(Z_NEAR, Z_FAR);
Matrix.multiplyMM(view, 0, eye.getEyeView(), 0, camera, 0);
Matrix.multiplyMM(modelView, 0, view, 0, modelMatrix, 0);
Matrix.multiplyMM(modelViewProjection, 0, projection, 0, modelView, 0);
Matrix.multiplyMV(lightPosInEyeSpace, 0, view, 0, lightPosInWorldSpace, 0);
normalMatrix = RPMath.getNormalMatrix3x3(modelView);
draw();
}
私有无效绘制():
private void draw() {
Log.i(TAG, "draw()");
// Bind shader program, VBO and IBO.
GLES20.glUseProgram(shaderProgramHandle);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboHandle);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, iboHandle);
// Set vertex shader uniforms.
GLES20.glUniformMatrix4fv(modelViewProjectionParam, 1, false, modelViewProjection, 0);
GLES20.glUniformMatrix4fv(modelViewParam, 1, false, modelView, 0);
GLES20.glUniformMatrix3fv(normalMatrixParam, 1, false, normalMatrix, 0);
if(boolPhongLightning) { // fragment shader uniforms.
vec = new float[3];
vec[0] = lightPosInEyeSpace[0];
vec[1] = lightPosInEyeSpace[1];
vec[2] = lightPosInEyeSpace[2];
GLES20.glUniform3fv(lightPosParam, 1, vec, 0);
}
// Set vertex attribute pointers.
attribOffset = 0;
GLES20.glVertexAttribPointer(positionParam, 4, GLES20.GL_FLOAT, false, attribStride, attribOffset);
if(boolTexCoords) {
attribOffset += 4 * BYTES_PER_FLOAT;
GLES20.glVertexAttribPointer(texCoordParam, 4, GLES20.GL_FLOAT, false, attribStride, attribOffset);
}
attribOffset += 4 * BYTES_PER_FLOAT;
GLES20.glVertexAttribPointer(normalParam, 4, GLES20.GL_FLOAT, false, attribStride, attribOffset);
// Enable vertex attribute arrays.
GLES20.glEnableVertexAttribArray(positionParam);
if(boolTexCoords) {
GLES20.glEnableVertexAttribArray(texCoordParam);
}
GLES20.glEnableVertexAttribArray(normalParam);
// Draw.
GLES20.glDrawElements(GLES20.GL_TRIANGLES, iboLength, GLES20.GL_UNSIGNED_SHORT, 0);
// Disable vertex attribute arrays.
GLES20.glDisableVertexAttribArray(positionParam);
if(boolTexCoords) {
GLES20.glDisableVertexAttribArray(texCoordParam);
}
GLES20.glDisableVertexAttribArray(normalParam);
// Unbind shader program, VBO and IBO.
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glUseProgram(0);
}
编辑:
Class RPVBO 和 RPIBO。有趣的部分是 init() 方法:
public final class RPVBO {
private static final String TAG = "RPVBO";
private static final int BYTES_PER_FLOAT = 4;
private int[] vboHandles;
private Vector<Float> vertices;
private int vboLength;
public RPVBO() {
Log.i(TAG, "Constructor()");
vboHandles = null;
vertices = new Vector();
vboLength = 0;
}
public void init() {
Log.i(TAG, "init()");
if((vboHandles != null) && (vboHandles[0] != 0)) {
return;
}
vboHandles = new int[1];
GLES20.glGenBuffers(1, vboHandles, 0);
if(vboHandles[0] != 0) {
float[] vertexArray = getVertexArray();
FloatBuffer clientSideBuffer = ByteBuffer.allocateDirect(vertexArray.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
clientSideBuffer.position(0);
clientSideBuffer.put(vertexArray);
clientSideBuffer.position(0);
if(clientSideBuffer.capacity() != vertexArray.length) {
Log.e(TAG, "init(): Error creating client side Floatbuffer.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating client side Floatbuffer.");
}
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboHandles[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, clientSideBuffer.capacity() * BYTES_PER_FLOAT, clientSideBuffer, GLES20.GL_STATIC_DRAW);
MainActivity.checkGLError(TAG, "init(): glBufferData()");
int[] params = new int[2];
GLES20.glGetBufferParameteriv(GLES20.GL_ARRAY_BUFFER, GLES20.GL_BUFFER_SIZE, params, 0);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glGetBufferParameteriv(GLES20.GL_ARRAY_BUFFER, GLES20.GL_BUFFER_USAGE, params, 1);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
if(params[0] != (clientSideBuffer.capacity() * BYTES_PER_FLOAT)) {
Log.e(TAG, "init(): Error setting OpenGL VBO data.");
throw new RuntimeException("Error: " + TAG + ": init(): Error setting OpenGL VBO data.");
}
Log.i(TAG, "init(): OpenGL VBO handle: " + vboHandles[0]);
Log.i(TAG, "init(): OpenGL VBO size in Bytes: " + params[0]);
switch(params[1]) {
case GLES20.GL_STREAM_DRAW:
Log.i(TAG, "init(): OpenGL VBO usage: GL_STREAM_DRAW.");
break;
case GLES20.GL_STATIC_DRAW:
Log.i(TAG, "init(): OpenGL VBO usage: GL_STATIC_DRAW.");
break;
case GLES20.GL_DYNAMIC_DRAW:
Log.i(TAG, "init(): OpenGL VBO usage: GL_DYNAMIC_DRAW.");
}
vertices.clear();
}
if(vboHandles[0] == 0) {
Log.e(TAG, "init(): Error creating VBO in OpenGL.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating VBO in OpenGL.");
}
}
public void addValue(final float value) {
vertices.add(value);
++vboLength;
}
public void addValue(final Float value) {
vertices.add(value);
++vboLength;
}
public void addArray(final float[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
vertices.add(array[i]);
++vboLength;
}
}
public void addArray(final Float[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
vertices.add(array[i]);
++vboLength;
}
}
public int getVboHandle() {
if(vboHandles == null) {
return 0;
}
return vboHandles[0];
}
@Nullable
public float[] getVertexArray() {
float[] array = new float[vertices.size()];
for(int i = 0; i < vertices.size(); i++) {
array[i] = vertices.get(i);
}
return array;
}
public int getVboLength() {
return vboLength;
}
}
public final class RPIBO {
private static final String TAG = "RPIBO";
private static final int BYTES_PER_SHORT = 2;
private int[] iboHandles;
private Vector<Short> indices;
private int iboLength;
public RPIBO() {
Log.i(TAG, "Constructor()");
iboHandles = null;
indices = new Vector();
iboLength = 0;
}
public void init() {
Log.i(TAG, "init()");
if((iboHandles != null) && (iboHandles[0] != 0)) {
return;
}
iboHandles = new int[1];
GLES20.glGenBuffers(1, iboHandles, 0);
if(iboHandles[0] != 0) {
short[] indexArray = getIndexArray();
ShortBuffer clientSideBuffer = ByteBuffer.allocateDirect(indexArray.length * BYTES_PER_SHORT).order(ByteOrder.nativeOrder()).asShortBuffer();
clientSideBuffer.position(0);
clientSideBuffer.put(indexArray);
clientSideBuffer.position(0);
if(clientSideBuffer.capacity() != indexArray.length) {
Log.e(TAG, "init(): Error creating client side Shortbuffer.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating client side Shortbuffer.");
}
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, iboHandles[0]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, clientSideBuffer.capacity() * BYTES_PER_SHORT, clientSideBuffer, GLES20.GL_STATIC_DRAW);
MainActivity.checkGLError(TAG, "init(): glBufferData()");
int[] params = new int[2];
GLES20.glGetBufferParameteriv(GLES20.GL_ELEMENT_ARRAY_BUFFER, GLES20.GL_BUFFER_SIZE, params, 0);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glGetBufferParameteriv(GLES20.GL_ELEMENT_ARRAY_BUFFER, GLES20.GL_BUFFER_USAGE, params, 1);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
if(params[0] != (clientSideBuffer.capacity() * BYTES_PER_SHORT)) {
Log.e(TAG, "init(): Error setting OpenGL IBO data.");
throw new RuntimeException("Error: " + TAG + ": init(): Error setting OpenGL IBO data.");
}
Log.i(TAG, "init(): OpenGL IBO handle: " + iboHandles[0]);
Log.i(TAG, "init(): OpenGL IBO size in Bytes: " + params[0]);
switch(params[1]) {
case GLES20.GL_STREAM_DRAW:
Log.i(TAG, "init(): OpenGL IBO usage: GL_STREAM_DRAW.");
break;
case GLES20.GL_STATIC_DRAW:
Log.i(TAG, "init(): OpenGL IBO usage: GL_STATIC_DRAW.");
break;
case GLES20.GL_DYNAMIC_DRAW:
Log.i(TAG, "init(): OpenGL IBO usage: GL_DYNAMIC_DRAW.");
}
indices.clear();
}
if(iboHandles[0] == 0) {
Log.e(TAG, "init(): Error creating IBO in OpenGL.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating IBO in OpenGL.");
}
}
public void addValue(final short value) {
indices.add(value);
++iboLength;
}
public void addValue(final Short value) {
indices.add(value);
++iboLength;
}
public void addArray(final short[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
indices.add(array[i]);
++iboLength;
}
}
public void addArray(final Short[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
indices.add(array[i]);
++iboLength;
}
}
public int getIboHandle() {
if(iboHandles == null) {
return 0;
}
return iboHandles[0];
}
@Nullable
public short[] getIndexArray() {
short[] array = new short[indices.size()];
for(int i = 0; i < indices.size(); i++) {
array[i] = indices.get(i);
}
return array;
}
public int getIboLength() {
return iboLength;
}
}
glVertexAttribPointer
(stride
)的第4个参数,指定连续通用顶点属性之间的字节偏移。
请参阅 Khronos OpenGL specification - Chapter 10.3. VERTEX ARRAYS,其中显示:
When sourcing vertex data from the buffer object, offset
specifies the offset in basic machine units of the first element in the vertex buffer. Pointers to the ith and (i + 1)
st elements of the array differ by stride
basic machine units, the pointer to the (i + 1)
st element being greater.
这意味着,如果您的顶点缓冲区包含顶点、纹理坐标和法向量,格式为:
vx, vy, vz, 1.0, s, t, 0.0, 1.0, nx, ny, nz, 1.0
那么stride
就得(4+4+4)*BYTES_PER_FLOAT = 48
,因为顶点的大小是4
,贴图坐标的大小是4
法向量是 4
.
纹理坐标的offset
为4*BYTES_PER_FLOAT = 16
,法向量的偏移量为(4+4)*BYTES_PER_FLOAT = 32
.
已解决。问题解决: attribStride
设置不正确
我正在编写一个小型 Google Cardboard 应用程序,它可以加载和渲染任何 Wavefront .obj 文件及其链接 .mtl 文件中表面的 material 属性。为了进行渲染,我使用了 VBO 和 IBO。对于表面和闪电的 material 属性,我使用了 phong 着色。
我的问题是,glDrawElements()
只会渲染由 12 个三角形组成的立方体的前两个三角形。
- 构成立方体的所有三角形都是逆时针排列的。
- 解析.obj文件生成的顶点数组和索引数组数据有效
- VBO客户端FloatBuffer和IBO客户端ShortBuffer的数据和长度有效。
glBufferData()
设置的VBO和IBO长度有效- 着色器已正确编译和附加。
glGetAttribLocation()
和glGetUniformLocation()
为着色器设置的所有属性和统一位置均有效。
我已经在调试器和 logcat 中检查了这些点。 我意识到它有很多代码。但是我在任何地方都找不到问题或问题的答案。所以请帮助 :D 首先感谢 :)
此方法设置 VBO、IBO、着色器程序、着色器程序位置、纹理和着色器程序统一值:
private void initializeGL() {
Log.i(TAG, "initializeGL()");
// Set camera matrix.
Matrix.setLookAtM(camera, OFFSET, EYE_POINT_X, EYE_POINT_Y, EYE_POINT_Z, CENTER_OF_VIEW_X, CENTER_OF_VIEW_Y, CENTER_OF_VIEW_Z, UP_VECTOR_X, UP_VECTOR_Y, UP_VECTOR_Z);
// Initialize shader program.
shaderProgram.init();
shaderProgramHandle = shaderProgram.getProgramHandle();
// Initialize object information.
objectInformation.init();
vboHandle = objectInformation.getVbo().getVboHandle();
vboLength = objectInformation.getVbo().getVboLength();
iboHandle = objectInformation.getIbo().getIboHandle();
iboLength = objectInformation.getIbo().getIboLength();
uniforms = objectInformation.getUniformArray();
numOfUniforms = objectInformation.getNumOfUniforms();
Log.i(TAG, "initializeGL(): Number of uniforms = " + numOfUniforms);
textures = objectInformation.getTextureArray();
numOfTextures = objectInformation.getNumOfTextures();
Log.i(TAG, "initializeGL(): Number of textures = " + numOfTextures);
// Get vertex shader uniform locations.
modelViewProjectionParam = GLES20.glGetUniformLocation(shaderProgramHandle, "u_ModelViewProjection");
Log.i(TAG, "initializeGL(): modelViewProjectionParam = " + modelViewProjectionParam);
modelViewParam = GLES20.glGetUniformLocation(shaderProgramHandle, "u_ModelView");
Log.i(TAG, "initializeGL(): modelViewParam = " + modelViewParam);
normalMatrixParam = GLES20.glGetUniformLocation(shaderProgramHandle, "u_NormalMatrix");
Log.i(TAG, "initializeGL(): normalMatrixParam = " + normalMatrixParam);
// Get vertex shader attribute locations.
positionParam = GLES20.glGetAttribLocation(shaderProgramHandle, "a_Position");
Log.i(TAG, "initializeGL(): positionParam = " + positionParam);
if(boolTexCoords) {
attribStride += 4 * BYTES_PER_FLOAT;
texCoordParam = GLES20.glGetAttribLocation(shaderProgramHandle, "a_TexCoord");
Log.i(TAG, "initializeGL(): texCoordParam = " + texCoordParam);
}
normalParam = GLES20.glGetAttribLocation(shaderProgramHandle, "a_Normal");
Log.i(TAG, "initializeGL(): normalParam = " + normalParam);
// Get fragment shader phong uniform locations.
if(boolPhongLightning) {
lightPosParam = GLES20.glGetUniformLocation(shaderProgramHandle, "lightPosition");
Log.i(TAG, "initializeGL(): lightPosParam = " + lightPosParam);
IaParam = GLES20.glGetUniformLocation(shaderProgramHandle, "Ia");
Log.i(TAG, "initializeGL(): IaParam = " + IaParam);
IpParam = GLES20.glGetUniformLocation(shaderProgramHandle, "Ip");
Log.i(TAG, "initializeGL(): IpParam = " + IpParam);
}
// Set fragment shader material uniforms.
GLES20.glUseProgram(shaderProgramHandle);
for(int i = 0; i < numOfUniforms; i++) {
String unifName = uniforms[i].getUnifName();
location = GLES20.glGetUniformLocation(shaderProgramHandle, unifName);
checkGLError(TAG, "initializeGL(): " + unifName + " shader location");
vec = uniforms[i].getArray();
if(vec != null) {
GLES20.glUniform3fv(location, 1, vec, 0);
Log.i(TAG, "initializeGL(): " + unifName + "_Param = " + location);
} else {
float value = uniforms[i].getValue();
GLES20.glUniform1f(location, value);
Log.i(TAG, "initializeGL(): " + unifName + "_Param = " + location);
}
}
// Set fragment shader phong uniforms.
if(boolPhongLightning) {
GLES20.glUniform3fv(IaParam, 1, Ia, 0);
GLES20.glUniform3fv(IpParam, 1, Ip, 0);
}
// Set fragment shader textures.
for(int i = 0; i < numOfTextures; i++) {
// Set active texture unit.
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
// Bind texture to active texture unit.
int texHandle = textures[i].getTextureHandle();
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texHandle);
// Assign texture unit to uniform in fragment shader.
String unifName = textures[i].getUnifName();
location = GLES20.glGetUniformLocation(shaderProgramHandle, unifName);
GLES20.glUniform1i(location, i);
checkGLError(TAG, "initializeGL(): glUniform1i()");
Log.i(TAG, "initializeGL(): " + unifName + "Param = " + location);
}
GLES20.glUseProgram(0);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glDepthFunc(GLES20.GL_LESS);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glFrontFace(GLES20.GL_CCW);
GLES20.glCullFace(GLES20.GL_BACK);
GLES20.glClearColor(0.3f, 0.3f, 0.3f, 0.1f);
}
public void onDrawEye():
@Override
public void onDrawEye(Eye eye) {
Log.i(TAG, "onDrawEye()");
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
projection = eye.getPerspective(Z_NEAR, Z_FAR);
Matrix.multiplyMM(view, 0, eye.getEyeView(), 0, camera, 0);
Matrix.multiplyMM(modelView, 0, view, 0, modelMatrix, 0);
Matrix.multiplyMM(modelViewProjection, 0, projection, 0, modelView, 0);
Matrix.multiplyMV(lightPosInEyeSpace, 0, view, 0, lightPosInWorldSpace, 0);
normalMatrix = RPMath.getNormalMatrix3x3(modelView);
draw();
}
私有无效绘制():
private void draw() {
Log.i(TAG, "draw()");
// Bind shader program, VBO and IBO.
GLES20.glUseProgram(shaderProgramHandle);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboHandle);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, iboHandle);
// Set vertex shader uniforms.
GLES20.glUniformMatrix4fv(modelViewProjectionParam, 1, false, modelViewProjection, 0);
GLES20.glUniformMatrix4fv(modelViewParam, 1, false, modelView, 0);
GLES20.glUniformMatrix3fv(normalMatrixParam, 1, false, normalMatrix, 0);
if(boolPhongLightning) { // fragment shader uniforms.
vec = new float[3];
vec[0] = lightPosInEyeSpace[0];
vec[1] = lightPosInEyeSpace[1];
vec[2] = lightPosInEyeSpace[2];
GLES20.glUniform3fv(lightPosParam, 1, vec, 0);
}
// Set vertex attribute pointers.
attribOffset = 0;
GLES20.glVertexAttribPointer(positionParam, 4, GLES20.GL_FLOAT, false, attribStride, attribOffset);
if(boolTexCoords) {
attribOffset += 4 * BYTES_PER_FLOAT;
GLES20.glVertexAttribPointer(texCoordParam, 4, GLES20.GL_FLOAT, false, attribStride, attribOffset);
}
attribOffset += 4 * BYTES_PER_FLOAT;
GLES20.glVertexAttribPointer(normalParam, 4, GLES20.GL_FLOAT, false, attribStride, attribOffset);
// Enable vertex attribute arrays.
GLES20.glEnableVertexAttribArray(positionParam);
if(boolTexCoords) {
GLES20.glEnableVertexAttribArray(texCoordParam);
}
GLES20.glEnableVertexAttribArray(normalParam);
// Draw.
GLES20.glDrawElements(GLES20.GL_TRIANGLES, iboLength, GLES20.GL_UNSIGNED_SHORT, 0);
// Disable vertex attribute arrays.
GLES20.glDisableVertexAttribArray(positionParam);
if(boolTexCoords) {
GLES20.glDisableVertexAttribArray(texCoordParam);
}
GLES20.glDisableVertexAttribArray(normalParam);
// Unbind shader program, VBO and IBO.
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glUseProgram(0);
}
编辑: Class RPVBO 和 RPIBO。有趣的部分是 init() 方法:
public final class RPVBO {
private static final String TAG = "RPVBO";
private static final int BYTES_PER_FLOAT = 4;
private int[] vboHandles;
private Vector<Float> vertices;
private int vboLength;
public RPVBO() {
Log.i(TAG, "Constructor()");
vboHandles = null;
vertices = new Vector();
vboLength = 0;
}
public void init() {
Log.i(TAG, "init()");
if((vboHandles != null) && (vboHandles[0] != 0)) {
return;
}
vboHandles = new int[1];
GLES20.glGenBuffers(1, vboHandles, 0);
if(vboHandles[0] != 0) {
float[] vertexArray = getVertexArray();
FloatBuffer clientSideBuffer = ByteBuffer.allocateDirect(vertexArray.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
clientSideBuffer.position(0);
clientSideBuffer.put(vertexArray);
clientSideBuffer.position(0);
if(clientSideBuffer.capacity() != vertexArray.length) {
Log.e(TAG, "init(): Error creating client side Floatbuffer.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating client side Floatbuffer.");
}
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboHandles[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, clientSideBuffer.capacity() * BYTES_PER_FLOAT, clientSideBuffer, GLES20.GL_STATIC_DRAW);
MainActivity.checkGLError(TAG, "init(): glBufferData()");
int[] params = new int[2];
GLES20.glGetBufferParameteriv(GLES20.GL_ARRAY_BUFFER, GLES20.GL_BUFFER_SIZE, params, 0);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glGetBufferParameteriv(GLES20.GL_ARRAY_BUFFER, GLES20.GL_BUFFER_USAGE, params, 1);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
if(params[0] != (clientSideBuffer.capacity() * BYTES_PER_FLOAT)) {
Log.e(TAG, "init(): Error setting OpenGL VBO data.");
throw new RuntimeException("Error: " + TAG + ": init(): Error setting OpenGL VBO data.");
}
Log.i(TAG, "init(): OpenGL VBO handle: " + vboHandles[0]);
Log.i(TAG, "init(): OpenGL VBO size in Bytes: " + params[0]);
switch(params[1]) {
case GLES20.GL_STREAM_DRAW:
Log.i(TAG, "init(): OpenGL VBO usage: GL_STREAM_DRAW.");
break;
case GLES20.GL_STATIC_DRAW:
Log.i(TAG, "init(): OpenGL VBO usage: GL_STATIC_DRAW.");
break;
case GLES20.GL_DYNAMIC_DRAW:
Log.i(TAG, "init(): OpenGL VBO usage: GL_DYNAMIC_DRAW.");
}
vertices.clear();
}
if(vboHandles[0] == 0) {
Log.e(TAG, "init(): Error creating VBO in OpenGL.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating VBO in OpenGL.");
}
}
public void addValue(final float value) {
vertices.add(value);
++vboLength;
}
public void addValue(final Float value) {
vertices.add(value);
++vboLength;
}
public void addArray(final float[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
vertices.add(array[i]);
++vboLength;
}
}
public void addArray(final Float[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
vertices.add(array[i]);
++vboLength;
}
}
public int getVboHandle() {
if(vboHandles == null) {
return 0;
}
return vboHandles[0];
}
@Nullable
public float[] getVertexArray() {
float[] array = new float[vertices.size()];
for(int i = 0; i < vertices.size(); i++) {
array[i] = vertices.get(i);
}
return array;
}
public int getVboLength() {
return vboLength;
}
}
public final class RPIBO {
private static final String TAG = "RPIBO";
private static final int BYTES_PER_SHORT = 2;
private int[] iboHandles;
private Vector<Short> indices;
private int iboLength;
public RPIBO() {
Log.i(TAG, "Constructor()");
iboHandles = null;
indices = new Vector();
iboLength = 0;
}
public void init() {
Log.i(TAG, "init()");
if((iboHandles != null) && (iboHandles[0] != 0)) {
return;
}
iboHandles = new int[1];
GLES20.glGenBuffers(1, iboHandles, 0);
if(iboHandles[0] != 0) {
short[] indexArray = getIndexArray();
ShortBuffer clientSideBuffer = ByteBuffer.allocateDirect(indexArray.length * BYTES_PER_SHORT).order(ByteOrder.nativeOrder()).asShortBuffer();
clientSideBuffer.position(0);
clientSideBuffer.put(indexArray);
clientSideBuffer.position(0);
if(clientSideBuffer.capacity() != indexArray.length) {
Log.e(TAG, "init(): Error creating client side Shortbuffer.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating client side Shortbuffer.");
}
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, iboHandles[0]);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, clientSideBuffer.capacity() * BYTES_PER_SHORT, clientSideBuffer, GLES20.GL_STATIC_DRAW);
MainActivity.checkGLError(TAG, "init(): glBufferData()");
int[] params = new int[2];
GLES20.glGetBufferParameteriv(GLES20.GL_ELEMENT_ARRAY_BUFFER, GLES20.GL_BUFFER_SIZE, params, 0);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glGetBufferParameteriv(GLES20.GL_ELEMENT_ARRAY_BUFFER, GLES20.GL_BUFFER_USAGE, params, 1);
MainActivity.checkGLError(TAG, "init(): glGetBufferParameteriv()");
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
if(params[0] != (clientSideBuffer.capacity() * BYTES_PER_SHORT)) {
Log.e(TAG, "init(): Error setting OpenGL IBO data.");
throw new RuntimeException("Error: " + TAG + ": init(): Error setting OpenGL IBO data.");
}
Log.i(TAG, "init(): OpenGL IBO handle: " + iboHandles[0]);
Log.i(TAG, "init(): OpenGL IBO size in Bytes: " + params[0]);
switch(params[1]) {
case GLES20.GL_STREAM_DRAW:
Log.i(TAG, "init(): OpenGL IBO usage: GL_STREAM_DRAW.");
break;
case GLES20.GL_STATIC_DRAW:
Log.i(TAG, "init(): OpenGL IBO usage: GL_STATIC_DRAW.");
break;
case GLES20.GL_DYNAMIC_DRAW:
Log.i(TAG, "init(): OpenGL IBO usage: GL_DYNAMIC_DRAW.");
}
indices.clear();
}
if(iboHandles[0] == 0) {
Log.e(TAG, "init(): Error creating IBO in OpenGL.");
throw new RuntimeException("Error: " + TAG + "init(): Error creating IBO in OpenGL.");
}
}
public void addValue(final short value) {
indices.add(value);
++iboLength;
}
public void addValue(final Short value) {
indices.add(value);
++iboLength;
}
public void addArray(final short[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
indices.add(array[i]);
++iboLength;
}
}
public void addArray(final Short[] array) {
final int arrayLength = array.length;
for(int i = 0; i < arrayLength; i++) {
indices.add(array[i]);
++iboLength;
}
}
public int getIboHandle() {
if(iboHandles == null) {
return 0;
}
return iboHandles[0];
}
@Nullable
public short[] getIndexArray() {
short[] array = new short[indices.size()];
for(int i = 0; i < indices.size(); i++) {
array[i] = indices.get(i);
}
return array;
}
public int getIboLength() {
return iboLength;
}
}
glVertexAttribPointer
(stride
)的第4个参数,指定连续通用顶点属性之间的字节偏移。
请参阅 Khronos OpenGL specification - Chapter 10.3. VERTEX ARRAYS,其中显示:
When sourcing vertex data from the buffer object,
offset
specifies the offset in basic machine units of the first element in the vertex buffer. Pointers to the ith and(i + 1)
st elements of the array differ bystride
basic machine units, the pointer to the(i + 1)
st element being greater.
这意味着,如果您的顶点缓冲区包含顶点、纹理坐标和法向量,格式为:
vx, vy, vz, 1.0, s, t, 0.0, 1.0, nx, ny, nz, 1.0
那么stride
就得(4+4+4)*BYTES_PER_FLOAT = 48
,因为顶点的大小是4
,贴图坐标的大小是4
法向量是 4
.
纹理坐标的offset
为4*BYTES_PER_FLOAT = 16
,法向量的偏移量为(4+4)*BYTES_PER_FLOAT = 32
.