我已经使用OpenGL 10 ES在3D中使用灯光渲染我的产品盒:
但现在我被卡住了。我的盒子都是51:63:24的设定比例。基于mm的物理设计。从像素的角度来看,这些比率是保持不变的,但在最终的屏幕2D渲染中它们会变得扭曲,取决于我使用的平板电脑,因为每个屏幕的x和y屏幕分辨率mDisplayMetrics.xdpi and mDisplayMetrics.ydpi
之间存在差异:
我可以看到最终的再现是通过以下方式对盒子的每个面进行的:
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, offset, 4);
我的问题是:如何以真实感知坐标而不是直接像素坐标将此渲染图制作到屏幕上?
通常情况下,我使用2D android.graphics.Matrix
来转换所有路径,形状,图像等以补偿屏幕分辨率的各向异性,但我无法在此处看到如何做到这一点。如果没有它,一切都会在y方向上进行纵向扩展/压缩,或者在x方向上进行横向扩展/压缩。对于我目前使用的Android平板电脑,此矩阵为 Matrix{[1.0, 0.0, 0.0][0.0, 0.9469214, 0.0][0.0, 0.0, 1.0]}
。好吧,它只有5.5%的均匀性,但如果我设计比例为51:63:24的盒子,我有点希望看到它们保持这个比例而不会因为它们旋转而扭曲。我绝对相信OpenGL和我提供的面部位图正在完美地完成它们的工作。这只是我需要允许的最终2D投影。但是如何?
任何人都可以告诉我如何进行最后的转型?基本上,我如何随意缩放最终的2D渲染结果?
MainActivity代码:
public class MyGLActivity extends AppCompatActivity {
public static FaceGroup facegroup;
private GLSurfaceView glView;
private DisplayMetrics mDisplayMetrics;
private Display mDisplay;
// use GLSurfaceView
// Call back when the activity is started, to initialize the view
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWHscalefactor();
**facegroup = new FaceGroup(this);**
glView = new GLSurfaceView(this); // Allocate a GLSurfaceView
glView.setRenderer(new MyGLRenderer()); // Use a custom renderer
this.setContentView(glView); // This activity sets to GLSurfaceView
}
// Call back when the activity is going into the background
@Override
protected void onPause() {
super.onPause();
glView.onPause();
}
// Call back after onPause()
@Override
protected void onResume() {
super.onResume();
glView.onResume();
}
private void getWHscalefactor() {
float unequalDPIscaleW, unequalDPIscaleH;
Matrix m = new Matrix();
mDisplayMetrics = new DisplayMetrics();
mDisplay = getWindowManager().getDefaultDisplay();
mDisplay.getMetrics(mDisplayMetrics);
unequalDPIscaleW = 1;
unequalDPIscaleH = xppi() / yppi();
if (unequalDPIscaleH > 1) {
unequalDPIscaleW = 1 / unequalDPIscaleH;
unequalDPIscaleH = 1;
}
m.setScale(unequalDPIscaleW, unequalDPIscaleH);
System.out.println("Anisotropy Matrix: " + m); }
public float xppi() { return mDisplayMetrics.xdpi; }
public float yppi() { return mDisplayMetrics.ydpi; }
调用 facegroup = new FaceGroup(this);
只是读取并解释从CorelDraw导出的SVG中提供的路径信息,生产盒专门用于打印,剪切,折叠和粘贴。他们是真实的:)。 facegroup
还会通过渲染这些路径来创建所需的面部位图。
MyGLRenderer的代码:
public class MyGLRenderer implements GLSurfaceView.Renderer {
private GAMZBox gamzBox; // (NEW)
private static float angleCube = 0; // rotational angle in degree for gamzBox
private static float speedCube = 0.25f; // rotational speed for gamzBox
// Constructor
public MyGLRenderer() {
gamzBox = new GAMZBox(); // (NEW)
}
// Call back when the surface is first created or re-created.
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set color's clear-value to black
gl.glClearDepthf(1.0f); // Set depth's clear-value to farthest
gl.glEnable(GL_DEPTH_TEST); // Enables depth-buffer for hidden surface removal
gl.glDepthFunc(GL10.GL_LEQUAL); // The type of depth testing to do
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); // nice perspective view
gl.glShadeModel(GL_SMOOTH); // Enable smooth shading of color
gl.glDisable(GL10.GL_DITHER); // Disable dithering for better performance
// Setup Texture, each time the surface is created (NEW)
gamzBox.loadTexture(gl); // Load images into textures (NEW)
gl.glEnable(GL10.GL_TEXTURE_2D); // Enable texture (NEW)
}
// Call back after onSurfaceCreated() or whenever the window's size changes
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if (height == 0) height = 1; // To prevent divide by zero
float aspect = (float)width / height;
// Set the viewport (display area) to cover the entire window
gl.glViewport(0, 0, width, height);
// Setup perspective projection, with aspect ratio matches viewport
gl.glMatrixMode(GL10.GL_PROJECTION); // Select projection matrix
gl.glLoadIdentity(); // Reset projection matrix
// Use perspective projection
GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.f);
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select model-view matrix
gl.glLoadIdentity(); // Reset
// You OpenGL|ES display re-sizing code here
// ......
}
// Call back to draw the current frame.
@Override
public void onDrawFrame(GL10 gl) {
// Clear color and depth buffers
float diffuseMaterial[] = { 0.5f, 0.5f, 0.5f, 1.0f };
float mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float light_position[] = { 0.0f, 1.0f, 2.0f, 0.0f };
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// ----- Render the Cube -----
gl.glLoadIdentity(); // Reset the model-view matrix
gl.glShadeModel(GL_SMOOTH);
gl.glEnable(GL_DEPTH_TEST);
gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial, 0);
gl.glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular, 0);
gl.glMaterialf(GL_FRONT, GL_SHININESS, 25f);
gl.glLightfv(GL_LIGHT0, GL_POSITION, light_position, 0);
gl.glEnable(GL_LIGHTING);
gl.glEnable(GL_LIGHT0);
//gl.glColorMaterial(GL_FRONT, GL_DIFFUSE);
gl.glEnable(GL_COLOR_MATERIAL);
gl.glTranslatef(0.0f, 0.0f, -5.0f); // Translate into the screen
gl.glRotatef(angleCube, 1f * (float)Math.sin(Math.toRadians(0.1 * angleCube)),
4.0f * (float)Math.sin(Math.toRadians(0.05 * angleCube)), -0.3f); // Rotate
gamzBox.draw(gl);
// Update the rotational angle after each refresh.
angleCube += speedCube;
}
}
和 GAMZBox
对象的面部定义和抽屉都在此代码中。它基本上是photo cube (https://www3.ntu.edu.sg/home/ehchua/programming/android/Android_3D.html)比照,以我的51:63:24面部比率建立。 bitmaps
在 FaceGroup
中定义为 public static Bitmap[] bitmaps = new Bitmap[6];
。
public class GAMZBox {
private FloatBuffer vertexBuffer; // Vertex Buffer
private FloatBuffer texBuffer; // Texture Coords Buffer
private int numFaces = 6;
private int[] textureIDs = new int[numFaces];
// private Bitmap[] bitmap = new Bitmap[numFaces];
private float cubeHalfSize = 24f / 63f; //1.2f;
// Constructor - Set up the vertex buffer
public GAMZBox() {
// Allocate vertex buffer. An float has 4 bytes
ByteBuffer vbb = ByteBuffer.allocateDirect(12 * 4 * numFaces);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
// Read images. Find the aspect ratio and adjust the vertices accordingly.
for (int face = 0; face < numFaces; face++) {
int imgWidth = bitmaps[face].getWidth();
int imgHeight = bitmaps[face].getHeight();
System.out.println(imgWidth + ", " + imgHeight);
float faceWidth = 2.0f;
float faceHeight = 2.0f;
if (face > 3) {
faceWidth *= 51f / 63f;
faceHeight *= 51f / 63f;
}
// Adjust for aspect ratio
if (imgWidth > imgHeight) {
faceHeight = faceHeight * imgHeight / imgWidth;
} else {
faceWidth = faceWidth * imgWidth / imgHeight;
}
float faceLeft = -faceWidth / 2;
float faceRight = -faceLeft;
float faceTop = faceHeight / 2;
float faceBottom = -faceTop;
// Define the vertices for this face
float[] vertices = {
faceLeft, faceBottom, 0.0f, // 0. left-bottom-front
faceRight, faceBottom, 0.0f, // 1. right-bottom-front
faceLeft, faceTop, 0.0f, // 2. left-top-front
faceRight, faceTop, 0.0f, // 3. right-top-front
};
vertexBuffer.put(vertices); // Populate
}
vertexBuffer.position(0); // Rewind
// Allocate texture buffer. An float has 4 bytes. Repeat for 6 faces.
float[] texCoords = {
0.0f, 1.0f, // A. left-bottom
1.0f, 1.0f, // B. right-bottom
0.0f, 0.0f, // C. left-top
1.0f, 0.0f // D. right-top
};
ByteBuffer tbb = ByteBuffer.allocateDirect(texCoords.length * 4 * numFaces);
tbb.order(ByteOrder.nativeOrder());
texBuffer = tbb.asFloatBuffer();
for (int face = 0; face < numFaces; face++) {
texBuffer.put(texCoords);
}
texBuffer.position(0); // Rewind
}
// Render the shape
public void draw(GL10 gl) {
gl.glFrontFace(GL10.GL_CCW);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer);
// front
gl.glPushMatrix();
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[0]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
gl.glPopMatrix();
// left
gl.glPushMatrix();
gl.glRotatef(270.0f, 0f, 1f, 0f);
gl.glTranslatef(0f, 0f, 51f/63f); // cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[1]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
gl.glPopMatrix();
// back
gl.glPushMatrix();
gl.glRotatef(180.0f, 0f, 1f, 0f);
gl.glTranslatef(0f, 0f, cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[2]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
gl.glPopMatrix();
// right
gl.glPushMatrix();
gl.glRotatef(90.0f, 0f, 1f, 0f);
gl.glTranslatef(0f, 0f, 51f/63f); // cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[3]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
gl.glPopMatrix();
// top
gl.glPushMatrix();
gl.glRotatef(270.0f, 1f, 0f, 0f);
gl.glTranslatef(0f, 0f, 1.0f); //cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[4]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
gl.glPopMatrix();
// bottom
gl.glPushMatrix();
gl.glRotatef(90.0f, 1f, 0f, 0f);
gl.glTranslatef(0f, 0f, 1.0f); //cubeHalfSize);
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[5]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
gl.glPopMatrix();
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
// Load images into 6 GL textures
public void loadTexture(GL10 gl) {
gl.glGenTextures(6, textureIDs, 0); // Generate texture-ID array for 6 IDs
// Generate OpenGL texture images
for (int face = 0; face < numFaces; face++) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureIDs[face]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
// Build Texture from loaded bitmap for the currently-bind texture ID
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmaps[face], 0);
bitmaps[face].recycle();
}
}
}
答案 0 :(得分:0)
对不起。我在阅读了如何小心避免最终投影ref:The Official Guide to Learning OpenGL, Version 1.1 Chapter 3: Viewing, subtitle: Viewport Transformation中的扭曲之后解决了这个问题。
我将 MyGLRenderer
更改为以下代码。 getWHscalefactor
计算所需的缩放系数( MScaleW
和 mScaleH
)以补偿x / y每英寸屏幕点的各向异性值并将其应用于:
gl.glViewport(0, 0, (int)(width * mScaleW), (int)(height * mScaleH));
OnSurfaceChanged
回调中的,同时保持 aspect
参数不变:
<强> GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.f);
强>
作为原始宽度与原始高度的比率。我的盒子现在看起来正确。 :)
我很抱歉。如果您这样做,这一切都可以删除。原谅一位老太太。顺便说一句,这不是一个聪明的事情。这是因为在形成一个问题,要求其他知情人士,像你一样,这有助于我组织我的想法,并清楚地看到真正的问题是什么。谢谢。 JosieH :)
public class MyGLRenderer implements GLSurfaceView.Renderer {
private GAMZBox gamzBox; // (NEW)
private static float angleCube = 0; // rotational angle in degree for gamzBox
private static float speedCube = 0.25f; // rotational speed for gamzBox
private float mScaleW, mScaleH;
// Constructor
public MyGLRenderer() {
gamzBox = new GAMZBox(); // (NEW)
}
// Call back when the surface is first created or re-created.
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
getWHscalefactor();
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Set color's clear-value to black
gl.glClearDepthf(1.0f); // Set depth's clear-value to farthest
gl.glEnable(GL_DEPTH_TEST); // Enables depth-buffer for hidden surface removal
gl.glDepthFunc(GL10.GL_LEQUAL); // The type of depth testing to do
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); // nice perspective view
gl.glShadeModel(GL_SMOOTH); // Enable smooth shading of color
gl.glDisable(GL10.GL_DITHER); // Disable dithering for better performance
// Setup Texture, each time the surface is created (NEW)
gamzBox.loadTexture(gl); // Load images into textures (NEW)
gl.glEnable(GL10.GL_TEXTURE_2D); // Enable texture (NEW)
}
// Call back after onSurfaceCreated() or whenever the window's size changes
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
if (height == 0) height = 1; // To prevent divide by zero
float aspect = (float)width / height;
// Set the viewport (display area) to cover the entire window
gl.glViewport(0, 0, (int)(width * mScaleW), (int)(height * mScaleH));
// Setup perspective projection, with aspect ratio matches viewport
gl.glMatrixMode(GL10.GL_PROJECTION); // Select projection matrix
gl.glLoadIdentity(); // Reset projection matrix
// Use perspective projection
GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.f);
gl.glMatrixMode(GL10.GL_MODELVIEW); // Select model-view matrix
gl.glLoadIdentity(); // Reset
// You OpenGL|ES display re-sizing code here
// ......
}
// Call back to draw the current frame.
@Override
public void onDrawFrame(GL10 gl) {
// Clear color and depth buffers
float diffuseMaterial[] = { 0.5f, 0.5f, 0.5f, 1.0f };
float mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float light_position[] = { 0.0f, 1.0f, 2.0f, 0.0f };
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
// ----- Render the Cube -----
gl.glLoadIdentity(); // Reset the model-view matrix
gl.glShadeModel(GL_SMOOTH);
gl.glEnable(GL_DEPTH_TEST);
gl.glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseMaterial, 0);
gl.glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular, 0);
gl.glMaterialf(GL_FRONT, GL_SHININESS, 25f);
gl.glLightfv(GL_LIGHT0, GL_POSITION, light_position, 0);
gl.glEnable(GL_LIGHTING);
gl.glEnable(GL_LIGHT0);
//gl.glColorMaterial(GL_FRONT, GL_DIFFUSE);
gl.glEnable(GL_COLOR_MATERIAL);
gl.glTranslatef(0.0f, 0.0f, -5.2f); // Translate into the screen
gl.glRotatef(angleCube, 1f * (float)Math.sin(Math.toRadians(0.1 * angleCube)),
4.0f * (float)Math.sin(Math.toRadians(0.05 * angleCube)), -0.3f); // Rotate
gamzBox.draw(gl);
// Update the rotational angle after each refresh.
angleCube += speedCube;
}
public void getWHscalefactor() {
DisplayMetrics dm = new DisplayMetrics();
Display d = theApp.getWindowManager().getDefaultDisplay();
d.getMetrics(dm);
boolean portrait = dm.heightPixels >= dm.widthPixels;
if (portrait) {
mScaleW = 1;
mScaleH = dm.xdpi / dm.ydpi;
if (mScaleH > 1) {
mScaleW = 1 / mScaleH;
mScaleH = 1;
}
} else {
mScaleW = dm.xdpi / dm.ydpi;
mScaleH = 1;
if (mScaleW > 1) {
mScaleH = 1 / mScaleW;
mScaleW = 1;
}
}
System.out.println("DPI Scales {" + mScaleW + ", " + mScaleH + "}");
}
}