在圆环上渲染人工制品
Rendering artefacts on torus
我正在尝试使用 OpenGL ES 2.0 在 Android 中渲染不透明圆环。当我添加颜色时,跟随 this guide, I noticed an artefact when viewing the torus from certain perspectives. I have linked an image that shows this, though the animation here 可能会更清楚。
初步阅读后,我认为这可能是深度缓冲区问题,因为看起来内部后表面可能被渲染在应该看到的外部前表面上。但是,更改视锥体 near/far 限制以尝试最大化表面之间的分离并没有帮助。
我确信顶点本身是正确的,从 rendering 使用 GLES20.GL_LINES
而不是 GLES20.GL_TRIANGLES
。知道是什么导致了这个人工制品吗?
下面是曲面的代码:
public class Miller {
private FloatBuffer verticesBuffer;
private ShortBuffer indicesBuffer;
final int nTheta = 50; // Number of divisions per 2pi theta.
final int nPhi = 50; // And per 2pi phi.
private int mProgramHandle;
private final int POSITION_DATA_SIZE_IN_ELEMENTS = 3; // Number of elements per coordinate per vertex (x,y,z)
private final int COLOR_DATA_SIZE_IN_ELEMENTS = 4; // Number of elements per colour per vertex (r,g,b,a)
private final int BYTES_PER_FLOAT = 4; // Number of bytes used per float.
private final int BYTES_PER_SHORT = 2; // Number of bytes used per short.
private final int POSITION_DATA_SIZE = POSITION_DATA_SIZE_IN_ELEMENTS * nTheta * nPhi;
private final int COLOR_DATA_SIZE = COLOR_DATA_SIZE_IN_ELEMENTS * nTheta * nPhi;
final int STRIDE = (POSITION_DATA_SIZE_IN_ELEMENTS + COLOR_DATA_SIZE_IN_ELEMENTS)* BYTES_PER_FLOAT;
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 aColor;" +
"attribute vec4 aPosition;" +
"varying vec4 vColor;" +
"void main() {" +
" vColor = aColor;" +
" gl_Position = uMVPMatrix * aPosition;" +
"}";
private float a; // Minor radius
private float R0; // Major radius
int nVertices = nTheta*nPhi; // Number of vertices
Miller(float minrad, float majrad) {
this.R0 = majrad/3.0f; // Rescale.
this.a = minrad/3.0f;
ByteBuffer buffer1 = ByteBuffer.allocateDirect(nVertices * (POSITION_DATA_SIZE_IN_ELEMENTS + COLOR_DATA_SIZE_IN_ELEMENTS) * BYTES_PER_FLOAT );
buffer1.order(ByteOrder.nativeOrder());
verticesBuffer = buffer1.asFloatBuffer();
for (int iTheta = 0; iTheta < nTheta; iTheta++) {
float theta = (float) (iTheta * 2 * Math.PI / nTheta);
for (int iPhi = 0; iPhi < nPhi; iPhi++) {
float phi = (float) (iPhi * 2 * Math.PI / nPhi);
// Circular torus vertices
float x = (float) ((R0 + a * Math.cos(theta)) * Math.cos(phi));
float y = (float) (a * Math.sin(theta));
float z = (float) ((R0 + a * Math.cos(theta)) * Math.sin(phi));
verticesBuffer.put(x);
verticesBuffer.put(y);
verticesBuffer.put(z);
float mod = (float)Math.sqrt(x*x + y*y + z*z); // Distance from origin to point
float cx = (float)Math.pow(Math.sin(phi),2);
float cy = (float)Math.pow(Math.sin(phi),2);
float cz = (float)Math.pow(Math.cos(phi),2); // colours
// Add colours according to position
verticesBuffer.put(cx);
verticesBuffer.put(cy);
verticesBuffer.put(cz);
verticesBuffer.put(1.0f); // Opaque
}
}
verticesBuffer.position(0);
// Create buffer for indices 2 bytes per short per coord per vertex
ByteBuffer buffer2 = ByteBuffer.allocateDirect(nPhi *nTheta * POSITION_DATA_SIZE_IN_ELEMENTS * BYTES_PER_SHORT * 2);
buffer2.order(ByteOrder.nativeOrder());
indicesBuffer = buffer2.asShortBuffer();
for (int iTheta = 0; iTheta < nTheta; iTheta++) {
for (int iPhi = 0; iPhi < nPhi; iPhi++) {
int f = iTheta* nPhi + iPhi; // First vertex
int s,fp1,sp1; // Initialise second, first plus 1, second plus 1.
if (iTheta != nTheta-1) { // Triangles that link back to theta=0
s = f + nPhi;
} else {
s = iPhi;
}
if (iPhi != nPhi-1) { // Triangles that link back to phi = 0
fp1 = f+1;
sp1 = s+1;
} else {
fp1 = f-iPhi;
sp1 = s-iPhi;
}
indicesBuffer.put((short)f); // First triangle
indicesBuffer.put((short)fp1);
indicesBuffer.put((short)s);
indicesBuffer.put((short)s); // Second triangle
indicesBuffer.put((short)fp1);
indicesBuffer.put((short)sp1);
}
}
indicesBuffer.position(0);
int vertexShaderHandle = TokGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, // Load vertex shader - acquire handle.
vertexShaderCode);
int fragmentShaderHandle = TokGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, // And fragment shader handle.
fragmentShaderCode);
// create empty OpenGL ES Program
mProgramHandle = GLES20.glCreateProgram();
// add the vertex shader to program
GLES20.glAttachShader(mProgramHandle, vertexShaderHandle);
// add the fragment shader to program
GLES20.glAttachShader(mProgramHandle, fragmentShaderHandle);
// Bind attributes
GLES20.glBindAttribLocation(mProgramHandle, 0, "aPosition");
GLES20.glBindAttribLocation(mProgramHandle, 1, "aColor");
// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgramHandle);
}
private int mPositionHandle;
private int mNormalHandle;
private int mColorHandle;
public void draw(float[] mvpMatrix) {
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgramHandle);
// get handle to vertex shader's aPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "aColor"); // USED TO BE vColor
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
// Set color for drawing the triangle
//GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Prepare the coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, STRIDE, verticesBuffer);
// Pass in the position information
verticesBuffer.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, verticesBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle); // Enable handle to position of vertices
// Pass in the colour information
verticesBuffer.position(POSITION_DATA_SIZE_IN_ELEMENTS);
GLES20.glVertexAttribPointer(mColorHandle, COLOR_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, verticesBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle); // Enable handle to colour of vertices
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Draw vertices linked by triangles.
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6*nTheta*nPhi, GLES20.GL_UNSIGNED_SHORT, indicesBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mColorHandle);
}
}
对于渲染器:
public class TokGLRenderer implements GLSurfaceView.Renderer {
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private Miller surf;
public void onSurfaceCreated(GL10 unused) {
surf = new Miller(0.96f, 3.1439243f);
}
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0,0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
float zoom = 0.9f;
Matrix.frustumM(mProjectionMatrix, 0, -ratio/zoom, ratio/zoom, -1f/zoom, 1f/zoom, 7f, 11f);
}
private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 5f, 5f, 5f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
surf.draw(mMVPMatrix);
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
要使深度测试正常工作,您必须启用深度测试 (GLES20.glEnable(GLES20.GL_DEPTH_TEST)
),并且必须指定深度缓冲区的大小。
在GLSurfaceView
this can be done by the 4th parameter of setEGLConfigChooser
中:
例如16 位的深度缓冲区大小:
setEGLConfigChooser(8, 8, 8, 8, 16, 0)
我正在尝试使用 OpenGL ES 2.0 在 Android 中渲染不透明圆环。当我添加颜色时,跟随 this guide, I noticed an artefact when viewing the torus from certain perspectives. I have linked an image that shows this, though the animation here 可能会更清楚。
初步阅读后,我认为这可能是深度缓冲区问题,因为看起来内部后表面可能被渲染在应该看到的外部前表面上。但是,更改视锥体 near/far 限制以尝试最大化表面之间的分离并没有帮助。
我确信顶点本身是正确的,从 rendering 使用 GLES20.GL_LINES
而不是 GLES20.GL_TRIANGLES
。知道是什么导致了这个人工制品吗?
下面是曲面的代码:
public class Miller {
private FloatBuffer verticesBuffer;
private ShortBuffer indicesBuffer;
final int nTheta = 50; // Number of divisions per 2pi theta.
final int nPhi = 50; // And per 2pi phi.
private int mProgramHandle;
private final int POSITION_DATA_SIZE_IN_ELEMENTS = 3; // Number of elements per coordinate per vertex (x,y,z)
private final int COLOR_DATA_SIZE_IN_ELEMENTS = 4; // Number of elements per colour per vertex (r,g,b,a)
private final int BYTES_PER_FLOAT = 4; // Number of bytes used per float.
private final int BYTES_PER_SHORT = 2; // Number of bytes used per short.
private final int POSITION_DATA_SIZE = POSITION_DATA_SIZE_IN_ELEMENTS * nTheta * nPhi;
private final int COLOR_DATA_SIZE = COLOR_DATA_SIZE_IN_ELEMENTS * nTheta * nPhi;
final int STRIDE = (POSITION_DATA_SIZE_IN_ELEMENTS + COLOR_DATA_SIZE_IN_ELEMENTS)* BYTES_PER_FLOAT;
// Use to access and set the view transformation
private int mMVPMatrixHandle;
private final String fragmentShaderCode =
"precision mediump float;" +
"varying vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private final String vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 aColor;" +
"attribute vec4 aPosition;" +
"varying vec4 vColor;" +
"void main() {" +
" vColor = aColor;" +
" gl_Position = uMVPMatrix * aPosition;" +
"}";
private float a; // Minor radius
private float R0; // Major radius
int nVertices = nTheta*nPhi; // Number of vertices
Miller(float minrad, float majrad) {
this.R0 = majrad/3.0f; // Rescale.
this.a = minrad/3.0f;
ByteBuffer buffer1 = ByteBuffer.allocateDirect(nVertices * (POSITION_DATA_SIZE_IN_ELEMENTS + COLOR_DATA_SIZE_IN_ELEMENTS) * BYTES_PER_FLOAT );
buffer1.order(ByteOrder.nativeOrder());
verticesBuffer = buffer1.asFloatBuffer();
for (int iTheta = 0; iTheta < nTheta; iTheta++) {
float theta = (float) (iTheta * 2 * Math.PI / nTheta);
for (int iPhi = 0; iPhi < nPhi; iPhi++) {
float phi = (float) (iPhi * 2 * Math.PI / nPhi);
// Circular torus vertices
float x = (float) ((R0 + a * Math.cos(theta)) * Math.cos(phi));
float y = (float) (a * Math.sin(theta));
float z = (float) ((R0 + a * Math.cos(theta)) * Math.sin(phi));
verticesBuffer.put(x);
verticesBuffer.put(y);
verticesBuffer.put(z);
float mod = (float)Math.sqrt(x*x + y*y + z*z); // Distance from origin to point
float cx = (float)Math.pow(Math.sin(phi),2);
float cy = (float)Math.pow(Math.sin(phi),2);
float cz = (float)Math.pow(Math.cos(phi),2); // colours
// Add colours according to position
verticesBuffer.put(cx);
verticesBuffer.put(cy);
verticesBuffer.put(cz);
verticesBuffer.put(1.0f); // Opaque
}
}
verticesBuffer.position(0);
// Create buffer for indices 2 bytes per short per coord per vertex
ByteBuffer buffer2 = ByteBuffer.allocateDirect(nPhi *nTheta * POSITION_DATA_SIZE_IN_ELEMENTS * BYTES_PER_SHORT * 2);
buffer2.order(ByteOrder.nativeOrder());
indicesBuffer = buffer2.asShortBuffer();
for (int iTheta = 0; iTheta < nTheta; iTheta++) {
for (int iPhi = 0; iPhi < nPhi; iPhi++) {
int f = iTheta* nPhi + iPhi; // First vertex
int s,fp1,sp1; // Initialise second, first plus 1, second plus 1.
if (iTheta != nTheta-1) { // Triangles that link back to theta=0
s = f + nPhi;
} else {
s = iPhi;
}
if (iPhi != nPhi-1) { // Triangles that link back to phi = 0
fp1 = f+1;
sp1 = s+1;
} else {
fp1 = f-iPhi;
sp1 = s-iPhi;
}
indicesBuffer.put((short)f); // First triangle
indicesBuffer.put((short)fp1);
indicesBuffer.put((short)s);
indicesBuffer.put((short)s); // Second triangle
indicesBuffer.put((short)fp1);
indicesBuffer.put((short)sp1);
}
}
indicesBuffer.position(0);
int vertexShaderHandle = TokGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER, // Load vertex shader - acquire handle.
vertexShaderCode);
int fragmentShaderHandle = TokGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, // And fragment shader handle.
fragmentShaderCode);
// create empty OpenGL ES Program
mProgramHandle = GLES20.glCreateProgram();
// add the vertex shader to program
GLES20.glAttachShader(mProgramHandle, vertexShaderHandle);
// add the fragment shader to program
GLES20.glAttachShader(mProgramHandle, fragmentShaderHandle);
// Bind attributes
GLES20.glBindAttribLocation(mProgramHandle, 0, "aPosition");
GLES20.glBindAttribLocation(mProgramHandle, 1, "aColor");
// creates OpenGL ES program executables
GLES20.glLinkProgram(mProgramHandle);
}
private int mPositionHandle;
private int mNormalHandle;
private int mColorHandle;
public void draw(float[] mvpMatrix) {
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgramHandle);
// get handle to vertex shader's aPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetAttribLocation(mProgramHandle, "aColor"); // USED TO BE vColor
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
// Set color for drawing the triangle
//GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Prepare the coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, STRIDE, verticesBuffer);
// Pass in the position information
verticesBuffer.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, verticesBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle); // Enable handle to position of vertices
// Pass in the colour information
verticesBuffer.position(POSITION_DATA_SIZE_IN_ELEMENTS);
GLES20.glVertexAttribPointer(mColorHandle, COLOR_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, verticesBuffer);
GLES20.glEnableVertexAttribArray(mColorHandle); // Enable handle to colour of vertices
// Pass the projection and view transformation to the shader
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Draw vertices linked by triangles.
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6*nTheta*nPhi, GLES20.GL_UNSIGNED_SHORT, indicesBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mColorHandle);
}
}
对于渲染器:
public class TokGLRenderer implements GLSurfaceView.Renderer {
// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
private final float[] mMVPMatrix = new float[16];
private final float[] mProjectionMatrix = new float[16];
private final float[] mViewMatrix = new float[16];
private Miller surf;
public void onSurfaceCreated(GL10 unused) {
surf = new Miller(0.96f, 3.1439243f);
}
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0,0, width, height);
float ratio = (float) width / height;
// this projection matrix is applied to object coordinates
// in the onDrawFrame() method
float zoom = 0.9f;
Matrix.frustumM(mProjectionMatrix, 0, -ratio/zoom, ratio/zoom, -1f/zoom, 1f/zoom, 7f, 11f);
}
private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 unused) {
// Redraw background color
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
// Set the camera position (View matrix)
Matrix.setLookAtM(mViewMatrix, 0, 5f, 5f, 5f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
// Calculate the projection and view transformation
Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
surf.draw(mMVPMatrix);
}
public static int loadShader(int type, String shaderCode){
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// add the source code to the shader and compile it
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
要使深度测试正常工作,您必须启用深度测试 (GLES20.glEnable(GLES20.GL_DEPTH_TEST)
),并且必须指定深度缓冲区的大小。
在GLSurfaceView
this can be done by the 4th parameter of setEGLConfigChooser
中:
例如16 位的深度缓冲区大小:
setEGLConfigChooser(8, 8, 8, 8, 16, 0)