Android の openGL ES 2.0 で球体を描画しようとしています。私はすでに関連する質問を見て、それらのコードのいくつかを試しましたが、それでも動作しません。
Android 開発者の例と、gamedev.net にあるこのコードに基づいて、以下のコードを思いつきました。ただし、正しく描画されていません。glDrawArrays()
レンダリングは機能するが、結果が正しくない場合、使用するglDrawElements()
とGL_INVALID_OPERATION
エラーが発生します。以下にバッファの内容をリストしました。
スフィア.java
public class Sphere
{
private int stacks;
private int slices;
private float radius;
//Buffers
private FloatBuffer vertexBuffer;
private FloatBuffer colorBuffer;
private ShortBuffer indexBuffer;
//Buffer sizes in aantal bytes
private int vertexBufferSize;
private int colorBufferSize;
private int indexBufferSize;
private int vertexCount;
private int program;
static final int FLOATS_PER_VERTEX = 3; // Het aantal floats in een vertex (x, y, z)
static final int FLOATS_PER_COLOR = 4; // Het aantal floats in een kleur (r, g, b, a)
static final int SHORTS_PER_INDEX = 2;
static final int BYTES_PER_FLOAT = 4;
static final int BYTES_PER_SHORT = 2;
static final int BYTES_PER_VERTEX = FLOATS_PER_VERTEX * BYTES_PER_FLOAT;
static final int BYTES_PER_COLOR = FLOATS_PER_COLOR * BYTES_PER_FLOAT;
static final int BYTES_PER_INDEX_ENTRY = SHORTS_PER_INDEX * BYTES_PER_SHORT;
// Set color with red, green, blue and alpha (opacity) values
private float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Sphere(float radius, int stacks, int slices)
{
this.stacks = stacks;
this.slices = slices;
this.radius = radius;
vertexCount = (stacks+1) * (slices+1);
vertexBufferSize = vertexCount * BYTES_PER_VERTEX;
colorBufferSize = vertexCount * BYTES_PER_COLOR;
indexBufferSize = vertexCount * BYTES_PER_INDEX_ENTRY;
program = GLHelpers.createProgram();
if (program == 0) {
return;
}
GLHelpers.checkGlError("program");
// Setup vertex-array buffer. Vertices in float. A float has 4 bytes.
vertexBuffer = ByteBuffer.allocateDirect(vertexBufferSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
colorBuffer = ByteBuffer.allocateDirect(colorBufferSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
indexBuffer = ByteBuffer.allocateDirect(indexBufferSize).order(ByteOrder.nativeOrder()).asShortBuffer();
generateSphereCoords(radius, stacks, slices);
vertexBuffer.position(0);
colorBuffer.position(0);
indexBuffer.position(0);
}
public void draw(float[] modelViewProjectionMatrix)
{
GLES20.glUseProgram(program);
GLHelpers.checkGlError("useprogram");
int positionHandle = GLES20.glGetAttribLocation(program, "a_Position");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, BYTES_PER_VERTEX, vertexBuffer);
GLHelpers.checkGlError("pos");
//int colorHandle = GLES20.glGetAttribLocation(program, "a_Color");
//GLES20.glEnableVertexAttribArray(colorHandle);
//GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, BYTES_PER_COLOR, colorBuffer);
//GLHelpers.checkGlError("color");
int matrixHandle = GLES20.glGetUniformLocation(program, "u_Matrix");
GLES20.glUniformMatrix4fv(matrixHandle, 1, false, modelViewProjectionMatrix, 0);
/*
* When using glDrawArrays rendering works but the results are not correct, when using glDrawElements I get an GL_INVALID_OPERATION error.
*/
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexBuffer.capacity(), GLES20.GL_SHORT, indexBuffer);
//GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLHelpers.checkGlError("draw");
// Disable vertex array
GLES20.glDisableVertexAttribArray(positionHandle);
//GLES20.glDisableVertexAttribArray(colorHandle);
}
private void generateSphereCoords(float radius, int stacks, int slices)
{
for (int stackNumber = 0; stackNumber <= stacks; ++stackNumber)
{
for (int sliceNumber = 0; sliceNumber < slices; ++sliceNumber)
{
float theta = (float) (stackNumber * Math.PI / stacks);
float phi = (float) (sliceNumber * 2 * Math.PI / slices);
float sinTheta = FloatMath.sin(theta);
float sinPhi = FloatMath.sin(phi);
float cosTheta = FloatMath.cos(theta);
float cosPhi = FloatMath.cos(phi);
vertexBuffer.put(new float[]{radius * cosPhi * sinTheta, radius * sinPhi * sinTheta, radius * cosTheta});
}
}
for (int stackNumber = 0; stackNumber < stacks; ++stackNumber)
{
for (int sliceNumber = 0; sliceNumber <= slices; ++sliceNumber)
{
indexBuffer.put((short) ((stackNumber * slices) + (sliceNumber % slices)));
indexBuffer.put((short) (((stackNumber + 1) * slices) + (sliceNumber % slices)));
}
}
}
}
GLHelpers.java
public class GLHelpers
{
private static final String TAG = "GLHelpers";
private static final String VERTEX_SHADER_CODE =
"uniform mat4 u_Matrix;" +
"attribute vec4 a_Position;" +
"attribute vec4 a_Color;" +
"varying vec4 v_Color;" +
"void main() {" +
" v_Color = a_Color;" +
" gl_Position = a_Position * u_Matrix;" +
"}";
private static final String FRAGMENT_SHADER_CODE =
"precision mediump float;" +
"varying vec4 v_Color;" +
"void main() {" +
" gl_FragColor = v_Color;" +
"}";
private static int loadShader(int shaderType, String source)
{
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0)
{
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
public static int createProgram()
{
int vertexShader = GLHelpers.loadShader(GLES20.GL_VERTEX_SHADER, GLHelpers.VERTEX_SHADER_CODE);
if (vertexShader == 0)
return 0;
int pixelShader = GLHelpers.loadShader(GLES20.GL_FRAGMENT_SHADER, GLHelpers.FRAGMENT_SHADER_CODE);
if (pixelShader == 0)
return 0;
int program = GLES20.glCreateProgram();
if (program != 0) {
GLES20.glAttachShader(program, vertexShader);
GLHelpers.checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
GLHelpers.checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE)
{
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
public static void checkGlError(String glOperation)
{
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR)
{
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
}
の内容vertexBuffer
X Y Z
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
-0.0, 0.0, 1.0,
-0.0, -0.0, 1.0,
0.0, -0.0, 1.0,
0.58778524, 0.0, 0.809017,
0.18163562, 0.559017, 0.809017,
-0.4755283, 0.34549147, 0.809017,
-0.4755282, -0.34549156, 0.809017,
0.18163571, -0.55901694, 0.809017,
0.95105654, 0.0, 0.30901697,
0.29389262, 0.90450853, 0.30901697,
-0.769421, 0.55901694, 0.30901697,
-0.76942086, -0.5590171, 0.30901697,
0.29389274, -0.9045085, 0.30901697,
0.9510565, 0.0, -0.30901703,
0.2938926, 0.9045085, -0.30901703,
-0.7694209, 0.5590169, -0.30901703,
-0.7694208, -0.55901706, -0.30901703,
0.29389274, -0.9045084, -0.30901703,
0.5877852, 0.0, -0.80901706,
0.1816356, 0.55901694, -0.80901706,
-0.47552824, 0.3454914, -0.80901706,
-0.47552818, -0.34549153, -0.80901706,
0.1816357, -0.5590169, -0.80901706,
-8.742278E-8, -0.0, -1.0,
-2.7015123E-8, -8.3144E-8, -1.0,
7.0726514E-8, -5.138581E-8, -1.0,
7.072651E-8, 5.138583E-8, -1.0,
-2.7015135E-8, 8.3143995E-8, -1.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0
の内容indexBuffer
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
を使用した場合の結果glDrawArrays()
: