Я пытаюсь визуализировать непрозрачный тор в Android, используя OpenGL ES 2.0. Когда я добавил цвет, следуя этому руководству, я заметил артефакт при просмотре тора с определенных точек зрения. У меня связано изображение, которое показывает это, хотя анимация здесь может сделать его более четким.
После некоторого начального прочтения я подумал, что это может быть проблемой буфера глубины, так как кажется, что внутренняя задняя поверхность может быть визуализирована поверх внешней передней поверхности, которая должна быть видна. Тем не менее, изменение точки обзора вблизи / далеко, чтобы попытаться максимизировать расстояние между поверхностями, не помогло.
Я уверен, что вершины сами по себе правильны, начиная с рендеринга с использованием 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;
}
}