OpenGLES进阶教程7-天空盒效果

教程

OpenGLES入门教程1-Tutorial01-GLKit OpenGLES入门教程2-Tutorial02-shader入门 OpenGLES入门教程3-Tutorial03-三维变换 OpenGLES入门教程4-Tutorial04-GLKit进阶 OpenGLES进阶教程1-Tutorial05-地球月亮 OpenGLES进阶教程2-Tutorial06-光线 OpenGLES进阶教程3-Tutorial07-粒子效果 OpenGLES进阶教程4-Tutorial08-帧缓存 OpenGLES进阶教程5-Tutorial09-碰碰车 OpenGLES进阶教程6-Tutorial10-平截体优化 这一次的内容是精心准备的天空盒特效,为了节约大家时间,这次在教程里面不贴代码,demo部分的内容都是干货。 写这个demo的过程中遇到了一些坎,最后会提到。 特别留意天空盒纹理坐标推导顶点数据对象切换

概念准备

天空盒特效:OpenGL ES提供了一个立方体贴图(cube mapping)的专门用于产生天空盒效果的纹理贴图模式。 举例:一个人,站在立方体的中间,上下左右前后看到的都是立方体的图片。

效果展示

为节省流量,gif比较模糊,清晰效果可以看demo。

核心思路

天空盒的核心就是通过方向来取样纹理,纹理坐标被当作方向向量,建立适合的正方体后,位置坐标就是纹理坐标。

具体细节

1、尺寸大小

天空盒的尺寸可以随意,但是需要足够大以容纳渲染的场景。 同时天空盒的中心要尽可能贴近视点的眼睛位置,避免太近产生纹理拉伸。

2、纹理坐标到纹素推导(核心)

纹理坐标(s, t, r)被当作方向向量看待,每个纹理单元都表示从原点所看到的纹理立方体上的图像。 如果是texture2D的情况,纹理坐标(s, t)会直接返回相应位置的纹素; textureCube的情况,首先读取cube纹理,然后以正方体中心为原点,(s,t,r)为方向,求出正方体和方向向量的交点位置(a, b),按照位置(a, b)在纹理上面选择相对应的纹素。 举个例子: 对于(s, t, r) , 假设 S=fabs(s) ,同理有T R。 如果S > T 并且 S > R。 那么可以确定交点在面+x 和 -x 根据s的±可以确定±x面。 直线过原点和点(s, t, r) ,那么也会过点(1, t/s, r/s)。 (t/s) 和(r/s)就是对应的纹素位置。 考虑到立方体的面为[-1, 1],那么可以把 (t/s + 1) / 2,这样就得到真正的纹素坐标( (t/s + 1) / 2, (r/s + 1) / 2)

3、顶点数据对象切换(核心)

  • glBindVertexArrayOES 很多应用会在同一个渲染帧调用多次glBindBuffer()、glEnableVertexAttribArray()和glVertexAttribPointer()函数(用不同的顶点属性来渲染多个对象) 新的顶点数据对象(VAO) 扩展会几率当前上下文中的与顶点属性相关的状态,并存储这些信息到一个小的缓存中。之后可以通过单次调用glBindVertexArrayOES() 函数来恢复,不需要在调用glBindBuffer()、glEnableVertexAttribArray()和glVertexAttribPointer()。
  • VAO和VBO VBO:顶点缓冲区对象(buffer-object),用于存储顶点坐标、纹理坐标、顶点法线、顶点颜色等。 VAO:顶点数据对象,记录顶点数据存储状态信息的状态对象(status-object)。

This extension introduces vertex array objects which encapsulate vertex array states on the server side (vertex buffer objects). These objects aim to keep pointers to vertex data and to provide names for different sets of vertex data. Therefore applications are allowed to rapidly switch between different sets of vertex array state, and to easily return to the default vertex array state. Q:Should vertex array objects be sharable across multiple OpenGL ES contexts? A: No.

总结

demo实现过程遇到最大的一个坑,如下:

bug.gif

暂停的适合,天空盒的效果会消失! 然后开始寻找问题所在,最后发现问题代码出现在这里

        // 增加角度
        if (!self.mPauseSwitch.on) {
            self.angle += 0.01;
        }

实在无法理解为什么角度的改变会影响天空盒的显示。 回顾了一下OpenGL ES的绘制过程,从顶点缓存到变换、着色到帧缓存,发现天空盒的绘制都没有问题。 接着开始思考,会不会是飞机的绘制影响了天空盒的绘制?因为这是两个着色器,存在不同的顶点数据和纹理。 于是尝试在绘制完天空盒后调用下面,防止天空盒绑定的数据缓存被飞机的影响。

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

在绘制完飞机后调用下面,防止飞机的顶点数据去影响到天空盒。

    glDisableVertexAttribArray(GLKVertexAttribPosition);
    glDisableVertexAttribArray(GLKVertexAttribNormal);
    glBindTexture(GL_TEXTURE_2D, 0);

<big>然而并没有什么用 ==!</big>

问题搁在心头好几天,每天都会尝试,也在google查GLKSkyboxEffect相关的问题,可惜GLKSkyboxEffect是苹果官方自己实现。 本来天空盒是上周就已经实现好,为了写这篇文章实现了一个暂停的功能,就出现这个bug。虽然去掉暂停功能很正常,但是demo里面存在问题。 经过很多天尝试后,已经可以确定的是,是飞机的绘制影响了天空盒的位置,角度的旋转只是隐藏了bug。 开始寻找非OpenGL ES的文章,看看OpenGL的天空盒实现,同时查看苹果官方的文档。 最后偶然在苹果的文档中看到一个关键词OES,我似乎明白了什么。 OES是OpenGL ES的一个非标准扩展,天空盒里面有用到,而我并没有处理。 尝试用OES来管理飞机的顶点模型。

//    glGenVertexArraysOES(1, &_mPositionBuffer);
//    glBindVertexArrayOES(_mPositionBuffer);

问题果然迎仍而解:暂停的时候天空盒是正常的。 接下来开始尝试不用OES,寻找问题的根源。 最后的结论:天空盒的绘制调用了glBindVertexArrayOES(),可是在绘制结束后没有glBindVertexArrayOES(0);导致飞机的顶点数据影响了天空盒的顶点数据。 解决方案:在绘制完天空盒后调用glBindVertexArrayOES(0);,问题完美解决。

Tips

天空盒还有两部分内容:一个是切图,这个比较简单,用CoreGraphics即可;另一个是用Shader来实现天空盒,而非GLKSkyboxEffect,这部分加进来篇幅就过长了。这两部分都在github上面放了源码,都已经编译调试没问题,下载完可以直接运行。 附上代码

思考题

  • 为什么视点距离天空盒太近会产生纹理拉伸?

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏鹅厂优文

游戏人工智能 读书笔记 (五) AI算法简介——树搜索

本书英文版: Artificial Intelligence and Games - A Springer Textbook

25550
来自专栏大数据文摘

手把手|如何用Python绘制JS地图?

587130
来自专栏落影的专栏

程序员算法基础——动态规划

23380
来自专栏工科狗和生物喵

【计算机本科补全计划】CCF 2016_09_04 交通规划 (Dijkstra - 单源最短路径算法)

具体的想法来自下面这篇写的很好的博客,当然,他的代码很复杂,不如我的精简,但是解释这个算法的手法比我好得多!

16020
来自专栏web前端教室

javascript 算法初识

最近有空,想学习下算法。一直感觉它很高深的样子,尤其我数学又不好。 但我还是想学学看,万一能学到点东西呢,,, 先来了解下算法的定义:是指解题方案的准确而完整的...

21060
来自专栏数据小魔方

动态地理信息可视化——leaflet在线地图简介

最近稍微涉猎了一下leaflet这个包,突然感到发现了动态可视化的新大门,这个包所提供的地图类型、动态效果、图层展示方式都大大扩展了ggplot作图系统的在数据...

56640
来自专栏用户2442861的专栏

买卖股票最多K次

给定一个大小为n的数组,数组的元素a[i]代表第i天的股票价格。 设计一个算法,计算在最多允许买卖k次(一买一卖记为一次)的条件下的最大收益。 需要注意的...

16330
来自专栏软件工程师成长笔记

高德,百度,Google地图定位偏移以及坐标系转换

一般用国际GPS纪录仪记录下来的经纬度,通过GPS定位拿到的原始经纬度,Google和高德地图定位的的经纬度(国外)都是基于WGS-84坐标系的;但是在国内是不...

1.5K30
来自专栏ml

2015编程之美(资格赛)--基站选址

题目3 : 基站选址 时间限制:2000ms 单点时限:1000ms 内存限制:256MB 描述 需要在一个N × M的网格中建立一个通讯基站,通讯基站仅必须建...

33450
来自专栏IT派

Pyecharts丨极其强大的Python数据可视化模块

昨天发了一篇关于【Matplotlib】的数据可视化文章,有一位小伙伴说可以直接使用Pyecharts,然后我就看了,发现确实,很强大,效果图也是非常的好看

51410

扫码关注云+社区

领取腾讯云代金券