我正在开发一个使用 OpenGL ES 2.0 的 Android 应用程序
因为这是我第一次使用 OpenGL(我曾经使用 WebGL),所以我制作了一个自定义且非常简单的 API,例如 THREE.js,它由 aObject3D
和Geometry
对象组成。
基本上,我所做的是:将形状存储在对象中,并使用其中的几何实例Geometry
创建对象。Mesh
另外,在里面Mesh
,我有:Vector3
object for: position
, scale
, rotation
.
我创建了一个圈子来测试,这就是正在发生的事情
如果我不改变任何东西,圆圈在屏幕上是完美的。如果我在创建圆时更改顶点位置,圆仍然可以。
但是,当我进行一些变换(更改属性位置、缩放或旋转)或 Object3D(在本例中为网格)时,圆变成了“拉伸”。
所以,我认为projectionMatrix有问题,但是不变换圆就可以了。
我的矩阵代码有问题吗?我应该将旋转、平移和缩放矩阵发送到 GPU 吗?
也许我把事情复杂化了,但由于这是我在阅读大量信息后第一次使用 OpenGL,这是可以接受的......
这是 Object3D 代码:
public class Object3D {
public Vector3 position = new Vector3();
public Vector3 rotation = new Vector3();
public Vector3 scale = new Vector3();
public Color color = new Color();
public float[] getMVMatrix(){
// Initialize matrix with Identity
float[] mvMatrix = new float[16];
Matrix.setIdentityM(mvMatrix, 0);
// apply scale
Matrix.scaleM(mvMatrix, 0, scale.x, scale.y, scale.z);
// set rotation
Matrix.setRotateM(mvMatrix, 0, rotation.x, 1f, 0, 0);
Matrix.setRotateM(mvMatrix, 0, rotation.y, 0, 1f, 0);
Matrix.setRotateM(mvMatrix, 0, rotation.z, 0, 0, 1f);
// apply translation
Matrix.translateM(mvMatrix, 0, position.x, position.y, position.z);
return mvMatrix;
}
}
这是几何类,它简化了三角形的使用:
public class Geometry {
// Public, to allow modifications
public ArrayList<Vector3> vertices;
public ArrayList<Face3> faces;
// Type of Geometry
public int triangleType = GLES20.GL_TRIANGLES;
[...]
public FloatBuffer getVerticesBuffer(){
if(verticesBuffer == null || verticesBufferNeedsUpdate){
/*
* Cache faces
*/
int size = vertices.size();
// (size of Vector3 list) * (3 for each object) * (4 bytes per float)
ByteBuffer bb = ByteBuffer.allocateDirect( size * 3 * 4 );
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// Get the ByteBuffer as a floatBuffer
verticesBuffer = bb.asFloatBuffer();
for(int i = 0; i < size; i++)
verticesBuffer.put(vertices.get(i).toArray());
verticesBufferNeedsUpdate = false;
}
verticesBuffer.position(0);
return verticesBuffer;
}
public ShortBuffer getFacesBuffer(){
if(facesBuffer == null || facesBufferNeedsUpdate){
/*
* Cache faces
*/
int size = faces.size();
// Log.i(TAG, "FACES Size: "+size);
// (size of Vector3 list) * (3 for each object) * (2 bytes per short)
ByteBuffer bb = ByteBuffer.allocateDirect( size * 3 * 2 );
// use the device hardware's native byte order
bb.order(ByteOrder.nativeOrder());
// Get the ByteBuffer as a floatBuffer
facesBuffer = bb.asShortBuffer();
for(int i = 0; i < size; i++)
facesBuffer.put(faces.get(i).toArray());
facesBufferNeedsUpdate = false;
}
facesBuffer.position(0);
return facesBuffer;
}
}
此外,Mesh 类,负责重新渲染Geometry
对象:
public class Mesh extends Object3D{
[...]
public void draw(float[] projectionMatrix, int shaderProgram){
float[] MVMatrix = getMVMatrix();
Matrix.multiplyMM(projectionMatrix, 0, projectionMatrix, 0, MVMatrix, 0);
// Check if geometry is set
if(geometry == null){
Log.i(TAG, "Geometry is null. skiping");
return;
}
// Add program to OpenGL environment
GLES20.glUseProgram(shaderProgram);
// Get, enable and Set the position attribute
positionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
GLES20.glEnableVertexAttribArray(positionHandle);
// Prepare the triangles coordinate data
Buffer vertexBuffer = geometry.getVerticesBuffer();
GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
COORDS_PER_VERTEX*4,
vertexBuffer);
// get handle to fragment shader's vColor member
int mColorHandle = GLES20.glGetUniformLocation(shaderProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color.toArray(), 0);
// get handle to shape's transformation matrix
int mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
ChwaziSurfaceView.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, projectionMatrix, 0);
ChwaziSurfaceView.checkGlError("glUniformMatrix4fv");
// Draw the triangles
if(geometry.triangleType == GLES20.GL_TRIANGLES){
Buffer indexesBuffer = geometry.getFacesBuffer();
GLES20.glDrawElements(
GLES20.GL_TRIANGLES,
geometry.faces.size()*3,
GL10.GL_UNSIGNED_SHORT,
indexesBuffer);
}else{
GLES20.glDrawArrays(geometry.triangleType, 0, geometry.vertices.size());
ChwaziSurfaceView.checkGlError("glDrawArrays");
}
// Disable vertex array
GLES20.glDisableVertexAttribArray(positionHandle);
}
}
这是我为测试它是否正常工作而制作的示例代码(只是翻译)
// Inside my Renderer...
@Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK);
// Set the camera position (View matrix)
Matrix.setLookAtM(mVMatrix, 0,
0, 0, -3,
0f, 0f, 0f,
0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);
// Create a rotation for the triangle
long time = SystemClock.uptimeMillis();// % 4000L;
myMesh.position.x = (time%4000)/4000f;
myMesh.draw(mMVPMatrix, shaderProgram.getProgram());
}
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
Matrix.orthoM(mProjMatrix, 0, -1, 1, -1, 1, 0, 10);
}
编辑
着色器代码:
private final String vertexShaderCode =
// This matrix member variable provides a hook to manipulate
// the coordinates of the objects that use this vertex shader
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
// the matrix must be included as a modifier of gl_Position
" gl_Position = vPosition * uMVPMatrix;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";