Android OpenGL ES->Cube Map

Cube Map,中文可以翻译成立方环境映射,下面是摘自维基百科的说明:

立方反射映射是用立方映射使得物体看起来如同在反射周围环境的一项技术。通常,这通常使用户外渲染中使用的 skybox 完成。尽管由于反射物周围的物体无法在结果中看到,所以这并不是一个真正的反射,但是通常仍然可以达到所期望的效果。

通过确定观察物体的向量就可以进行立方映射反射,照相机光线在照相机向量与物体相交的位置按照曲面法线方向进行反射,这样传到立方图(cube map)取得纹素(texel)的反射光线在照相机看来好像位于物体表面,这样就得到了物体的反射效果。

简单的讲,就是你把一个具有金属反射特性的茶壶放在一个房间中,茶壶的金属表面会反射房间的场景,Cube Map就是解决如何将场景(环境)的内容显示在茶壶的表面,如下图所示:

本例使用环面(Torus)做为反射的表面,在OpenGL ES中任何3D物体,最终都是通过三角形来构造的,本例代码generateTorusGrid 和Grid对象用来构造环面的顶点坐标。具体算法有兴趣的可以自行研究(需要有立体几何的知识,这里不详细解释)。

Cube map技术说到底就是用一个虚拟的立方体(cube)包围住物体,眼睛到物体某处的向量eyevec经过反射(以该处的法线为对称轴),反射向量reflectvec射到立方体上,就在该立方体上获得一个纹素了(见下图)。明显,我们需要一个类似天空盒般的6张纹理贴在这个虚拟的立方体上。按CUBE MAPPING原意,就是一种enviroment map,因此把周围场景渲染到这6张纹理里是“正统”的。也就是每次渲染时,都作一次离线渲染,分别在每个矩形中心放置相机“拍下”场景,用FBO渲染到纹理,然后把这张纹理作为一个cube map对象的六纹理之一。这样即使是动态之物也能被映射到物体表面了(虽然缺点是不能映射物体自身的任何部分)。

本例使用的六张图为res/raw 目录下的 skycubemap0 — skycubemap5 ,如下图所示

使用Cube Map,首先要检测设备是否支持Cube Map 材质,本例使用以下代码检测设备是否支持Cube Map。

private boolean checkIfContextSupportsCubeMap(GL10 gl) {
return checkIfContextSupportsExtension(gl, "GL_OES_texture_cube_map");
 
}
 
/**
* This is not the fastest way to check for an extension, but fine if
* we are only checking for a few extensions each time a context is created.
* @param gl
* @param extension
* @return true if the extension is present in the current context.
*/
private boolean checkIfContextSupportsExtension(GL10 gl, String extension) {
String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " ";
// The extensions string is padded with spaces between extensions, but not
// necessarily at the beginning or end. For simplicity, add spaces at the
// beginning and end of the extensions string and the extension string.
// This means we can avoid special-case checks for the first or last
// extension, as well as avoid special-case checks when an extension name
// is the same as the first part of another extension name.
return extensions.indexOf(" " + extension + " ") >= 0;
}


 

Cube Map (使用6张图),处调用设置Cube Map外,其基本使用步骤类似于普通材质的使用。本例使用资源,其设置Cube Map的基本步骤如下:

1. 调入图像资源

if (mContextSupportsCubeMap) {
int[] cubeMapResourceIds = new int[]{
R.raw.skycubemap0, R.raw.skycubemap1, R.raw.skycubemap2,
R.raw.skycubemap3, R.raw.skycubemap4, R.raw.skycubemap5};
mCubeMapTextureID = generateCubeMap(gl, cubeMapResourceIds);
}
 
....
 
private int generateCubeMap(GL10 gl, int[] resourceIds) {
checkGLError(gl);
int[] ids = new int[1];
gl.glGenTextures(1, ids, 0);
int cubeMapTextureId = ids[0];
gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, cubeMapTextureId);
gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP,
GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
 
for (int face = 0; face < 6; face++) {
InputStream is = getResources().openRawResource(resourceIds[face]);
Bitmap bitmap;
try {
bitmap = BitmapFactory.decodeStream(is);
} finally {
try {
is.close();
} catch(IOException e) {
Log.e("CubeMap", "Could not decode texture for face " + Integer.toString(face));
}
}
GLUtils.texImage2D(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0,
bitmap, 0);
bitmap.recycle();
}
checkGLError(gl);
return cubeMapTextureId;
}


 

2. 绑定材质

函数generateCubeMap返回一个Texture的ID,在OpenGL ES中使用材质时,需要绑定材质

gl.glActiveTexture(GL10.GL_TEXTURE0);
checkGLError(gl);
gl.glEnable(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP);
checkGLError(gl);
gl.glBindTexture(GL11ExtensionPack.GL_TEXTURE_CUBE_MAP, mCubeMapTextureID);
checkGLError(gl);
GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
gl11ep.glTexGeni(GL11ExtensionPack.GL_TEXTURE_GEN_STR,
GL11ExtensionPack.GL_TEXTURE_GEN_MODE,
GL11ExtensionPack.GL_REFLECTION_MAP);
checkGLError(gl);
gl.glEnable(GL11ExtensionPack.GL_TEXTURE_GEN_STR);
checkGLError(gl);
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_DECAL);
...
mGrid.draw(gl);
...
gl.glDisable(GL11ExtensionPack.GL_TEXTURE_GEN_STR);


 

这样就给环面物体添加了环境材质,显示结果如下:

 

#ifndef CTESTIMAGE_H #define CTESTIMAGE_H #include <QGraphicsItem> #include <QGraphicsScene> #include <QOpenGLTexture> #include <QOpenGLShaderProgram> #include <QOpenGLBuffer> #include <QOpenGLVertexArrayObject> #include <QOpenGLFunctions_3_0> #include <QPainter> #include <QOpenGLWidget> #include <QMatrix4x4> #include <QTransform> #include <QGraphicsView> #include <QImage> #include <QGraphicsItem> class CTestImage : public QGraphicsPixmapItem { public: CTestImage(); QRectF boundingRect() const; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); private: void initialize(); void paint(); private: QOpenGLShaderProgram* _program; QOpenGLTexture* _texture; }; #endif // CTESTIMAGE_H #include "CTestImage.h" CTestImage::CTestImage() : _program(0), _texture(0) { setPixmap(QPixmap("/boxes/boxes/cubemap_posz.jpg")); } QRectF CTestImage::boundingRect() const { return QRect(0, 0, 3072, 3072); } void CTestImage::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->beginNativePainting(); glClear(GL_COLOR_BUFFER_BIT); paint(); painter->endNativePainting(); } void CTestImage::initialize() { prepareGeometryChange(); const char *vertexShaderTex16Source = "#version 300 es \r" "layout (location = 0) in vec3 aPos; \r" "layout (location = 1) in vec2 aTexCoord; \r" "uniform mat4 transform; \r" "out vec2 TexCoord; \r" "void main() \r" "{ \r" " gl_Position = transform * vec4(aPos, 1.0); \r" " TexCoord = aTexCoord; \r" "} "; const char *fragmentShaderTex16Source = "#version 300 es \r" "precision mediump float; \r" "out vec4 FragColor; \r" "in vec2 TexCoord; \r" "uniform sampler2D ourTexture; \r" "void main() \r" "{ \r" " FragColor = texture(ourTexture, TexCoord); \r" "} "; QImage img("/boxes/boxes/cubemap_posz.jpg"); img = img.scaled(3072, 3072, Qt::IgnoreAspectRatio); _texture = new QOpenGLTexture(img); QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex); vshader->compileSourceCode(vertexShaderTex16Source); QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment); fshader->compileSourceCode(fragmentShaderTex16Source); // Program _program = new QOpenGLShaderProgram; _program->addShader(vshader); _program->addShader(fshader); _program->bindAttributeLocation("aPos", 0); _program->bindAttributeLocation("aTexCoord", 1); _program->link(); _program->bind(); _program->setUniformValue("ourTexture", 0); } void CTestImage::paint() { if (_program == nullptr) { initialize(); } if (_program) { _program->bind(); QMatrix4x4 proj; glGetFloatv(GL_PROJECTION_MATRIX, proj.data()); QMatrix4x4 model; glGetFloatv(GL_MODELVIEW_MATRIX, model.data()); QMatrix4x4 projMatrix; projMatrix.ortho(this->scene()->views().at(0)->viewport()->rect()); QMatrix4x4 test = proj * model; _program->setUniformValue("transform", test); /* 长方形 */ const static GLfloat verticer[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, }; const static GLfloat verticesTEX[] = { // - 纹理坐标 - 0.0f, 1.0f, // 左上 1.0f, 1.0f, // 右上 1.0f, 0.0f, // 右下 0.0f, 0.0f, // 左下 }; _program->enableAttributeArray(0); _program->enableAttributeArray(1); _program->setAttributeArray(0, GL_FLOAT, verticer, 3, 3*sizeof(GLfloat)); _program->setAttributeArray(1, GL_FLOAT, verticesTEX, 2, 2*sizeof(GLfloat)); _texture->bind(); glDrawArrays(GL_QUADS, 0, 4); // 6 vertices per quad switch (glGetError()) { case GL_NO_ERROR: qDebug() << QObject::tr("No error."); break; case GL_INVALID_ENUM: qDebug() << QObject::tr("Invalid enum."); break; case GL_INVALID_VALUE: qDebug() << QObject::tr("Invalid value."); break; case GL_INVALID_OPERATION: qDebug() << QObject::tr("Invalid operation."); break; case GL_STACK_OVERFLOW: qDebug() << QObject::tr("Stack overflow."); break; case GL_STACK_UNDERFLOW: qDebug() << QObject::tr("Stack underflow."); break; case GL_OUT_OF_MEMORY: qDebug() << QObject::tr("Out of memory."); break; default: qDebug() << QObject::tr("Unknown error."); break; } } } 为什么这段代码绘画不出图片
最新发布
06-08
### OpenGL无法绘制图片问题的解决方案 在使用`QOpenGLShaderProgram`和`QOpenGLTexture`时,如果遇到无法绘制图片的问题,可能涉及多个方面的原因。以下是详细的分析与解决方案: #### 1. 着色器代码检查 确保着色器代码正确无误,并且已成功编译链接。例如,顶点着色器和片段着色器应正确设置纹理采样器[^1]。 ```glsl // 顶点着色器 #version 330 core in vec2 position; in vec2 texCoord; out vec2 TexCoord; void main() { gl_Position = vec4(position, 0.0, 1.0); TexCoord = texCoord; } ``` ```glsl // 片段着色器 #version 330 core in vec2 TexCoord; out vec4 FragColor; uniform sampler2D texture1; void main() { FragColor = texture(texture1, TexCoord); } ``` #### 2. 纹理加载与绑定 确保纹理对象已正确创建并绑定到`QOpenGLTexture`中。以下是一个示例代码片段,展示如何加载纹理并绑定到着色器中[^2]。 ```python from PyQt5.QtGui import QOpenGLShaderProgram, QOpenGLTexture # 加载纹理 texture = QOpenGLTexture(QImage("image.png").mirrored()) texture.setMinificationFilter(QOpenGLTexture.LinearMipMapLinear) texture.setMagnificationFilter(QOpenGLTexture.Linear) # 绑定纹理到着色器 program = QOpenGLShaderProgram() program.bind() texture.bind() program.setUniformValue("texture1", 0) # 确保 uniform 名称匹配 ``` #### 3. 数据传递与缓冲区设置 确保顶点数据和纹理坐标已正确传递给OpenGL缓冲区。以下是一个完整的顶点数据示例[^3]。 ```python vertices = np.array([ -0.5, 0.5, 0.0, 1.0, # 左上角 0.5, 0.5, 1.0, 1.0, # 右上角 -0.5, -0.5, 0.0, 0.0, # 左下角 0.5, -0.5, 1.0, 0.0 # 右下角 ], dtype=np.float32) vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer) vbo.create() vbo.bind() vbo.allocate(vertices.tobytes(), vertices.nbytes) ``` #### 4. 渲染逻辑 确保渲染逻辑中调用了正确的绘制命令。例如,使用`glDrawArrays`或`glDrawElements`进行绘制[^4]。 ```python program.setAttributeBuffer("position", GL_FLOAT, 0, 2, 4 * sizeof(GLfloat)) program.setAttributeBuffer("texCoord", GL_FLOAT, 2 * sizeof(GLfloat), 2, 4 * sizeof(GLfloat)) program.enableAttributeArray("position") program.enableAttributeArray("texCoord") glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) ``` #### 5. 错误调试 如果上述步骤均无误,但仍无法绘制图片,则需要检查OpenGL错误日志。可以使用`glGetError`来捕获潜在问题[^5]。 ```python error = glGetError() if error != GL_NO_ERROR: print(f"OpenGL Error: {error}") ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值