我正在做一些 3D 建模,这需要一些稍微不寻常的变换(本质上我需要进行倾斜/倾斜变换而不是旋转)。由于我之前没有进行过任何 opengl 编程,因此我使用了 2D canvas 绘图库并创建了自己的 3D -> 2D 转换矩阵以进行概念验证。使用线框模型效果很好。
为了将其转换为 3D,我使用了 SDK 示例 OpenGLS20Complete 程序来绘制一个正方形和一个三角形。我修改了它,将绘制方法抽象为接口(interface) (IDrawable),这样我就可以处理 IDrawable 列表。为了进行测试,我通过形成一个正方形列表来构建一个具有 6 个面的立方体。
一切正常,除了立方体显然是用透视绘制的,因为顶面比底面大。 OrthoM 返回一个不同的矩阵到 frustumM (矩阵值不同),但据我所知正在做同样的事情。我需要这个应用程序的正交透视图(即没有透视图)。我无法让它工作。
我已经剥离了代码以显示单个多维数据集以进行测试,如下所示:
OpenGLS20Complete onSurfaceCreated中的代码是:
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Set the background frame color
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
transformedshapes = new ArrayList<IDrawable>();
cubeSide = 0.3f;
IDrawable cube1 = new Cube(-0.4f, cubeSide, -0.4f, cubeSide, 0f, cubeSide, Color.GRAY, Color.RED, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.CYAN);
transformedshapes.add(cube1);
// next line added by me
GLES20.glEnable( GLES20.GL_DEPTH_TEST );
}
Cube 构造函数 Cube(left,cubewidth,right,cubelength,basez,cubeheight,...六种颜色)仅创建 6 个面(作为 IDrawable 对象),即列出的 6 种颜色中的每一种。做它应该做的事。
OnSurfaceChanged 代码是:
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
// Adjust the viewport based on geometry changes,
// such as screen rotation
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / (float) height;
// Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7); // original
Matrix.orthoM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
无论我使用 .frustumM 还是 .orthoM 方法,屏幕上的结果都是相同的。正如我所期望的那样,更改 frustumM 方法中的值会更改显示。使用调试器,我可以看到 frustumM 和 orthoM 方法准备不同的矩阵。
OnDraw 方法如下所示:
@Override
public void onDrawFrame(GL10 unused) {
// Draw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// 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);
// Draw untransformed shapes
for (IDrawable shape:untransformedshapes)
shape.draw(mMVPMatrix);
float [] rotatedmMVPMatrix = new float[16];
float netRotationAboutX = mAngleRotateAboutX - mAngleStartRotateAboutX;
float netRotationAboutY = mAngleRotateAboutY - mAngleStartRotateAboutY;
float netRotationAboutZ = mAngleRotateAboutZ - mAngleStartRotateAboutZ;
Matrix.setRotateM(mRotationMatrix, 0, netRotationAboutY, 0, -1f, 0f);
float[] xRotationMatrix = new float[16];
Matrix.setRotateM(xRotationMatrix, 0, netRotationAboutX, -1f, 0, 0f);
Matrix.multiplyMM(mRotationMatrix, 0, xRotationMatrix, 0,
mRotationMatrix, 0);
// Combine the rotation matrix with the projection and camera view
Matrix.multiplyMM(rotatedmMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
for (IDrawable shape : transformedshapes)
shape.draw(rotatedmMVPMatrix);
}
上面执行的是旋转而不是倾斜矩阵,以使其更易于调试。它有效,因为当我更改 mAngleRotateAboutX 和 mAngleRotateAboutY 时,一个或多个立方体会旋转。
.setLookAtM() 方法似乎并不完全适合正交投影。由于不需要透视,z 距离(在本例中为 3)似乎没有必要。正交投影应该只需要方向(即 2 个自变量),而不需要 View 位置(3 个自变量)。也许我不应该使用这个?
作为我的立方体一部分的正方形是 PWRectShape 类的实例,该类实现了我的 IDrawable 接口(interface)的绘制方法。这是正方形绘制方法OpenGLS20Complete的monkey-see-monkey-do修改
class PWRectShape implements IDrawable{
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;" +
"}";
private final FloatBuffer vertexBuffer;
private final ShortBuffer drawListBuffer;
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
private int mMVPMatrixHandle;
// number of coordinates per vertex in this array
static final int COORDS_PER_VERTEX = 3;
private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
// Set color with default red, green, blue and alpha (opacity) values
float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };
public PWRectShape(float [] rectCoords, int colour) { // the constructor
// initialize vertex byte buffer for shape coordinates
color[0] = (float)Color.red(colour)/256f;
color[1] = (float)Color.green(colour)/256f;
color[2] = (float)Color.blue(colour)/256f;
color[3] = 1f;
ByteBuffer bb = ByteBuffer.allocateDirect(
// (# of coordinate values * 4 bytes per float)
rectCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(rectCoords);
vertexBuffer.position(0);
// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(
// (# of coordinate values * 2 bytes per short)
drawOrder.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);
// prepare shaders and OpenGL program
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
mProgram = GLES20.glCreateProgram(); // create empty OpenGL Program
GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
GLES20.glLinkProgram(mProgram); // create OpenGL program executables
}
public void draw(float[] mvpMatrix) { // the draw method
// Add program to OpenGL environment
GLES20.glUseProgram(mProgram);
// get handle to vertex shader's vPosition member
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// Enable a handle to the triangle vertices
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// get handle to fragment shader's vColor member
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// Set color for drawing the triangle
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// get handle to shape's transformation matrix
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
MyGLRenderer.checkGlError("glGetUniformLocation");
// Apply the projection and view transformation
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
MyGLRenderer.checkGlError("glUniformMatrix4fv");
// Draw the square
GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length,
GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
// Disable vertex array
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
还有用于设置简单着色器等的附加代码,所有这些代码与 OpenGLES20Complete 相比都没有变化。如果有需要我可以发帖。但这一切似乎都运作良好。事实上,一切正常 - 我可以在多个位置绘制多个立方体,甚至可以通过自定义投影矩阵正确倾斜它们。唯一不起作用的是立方体的顶面比底面大,这显然是使用透视,无论我使用 .frustumM 还是 .orthoM。
关于为什么运行此代码时我得到透视图而不是正交投影有什么建议或想法吗?
最佳答案
不确定问题两年后的答案有多有用 - 但它可能对其他人有帮助。
在您的 OnDrawFrame()
方法中,您有以下内容:
Matrix.multiplyMM(rotatedmMVPMatrix, 0, mRotationMatrix, 0, mMVPMatrix, 0);
而我认为您打算首先应用旋转变换来变换场景中的对象,然后通过 View 和投影变换来变换结果。
这需要将 LHS 和 RHS 的顺序颠倒,如下所示:
Matrix.multiplyMM(rotatedmMVPMatrix, 0, mMVPMatrix, 0, mRotationMatrix, 0);
我怀疑你知道,当你 multiply matrices它生成一个新矩阵,相当于首先将 RHS 应用于输入,然后将 LHS 应用于结果。
关于java - 对我来说,orthoM 与 frustumM 做同样的事情(即显示透视),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18504405/