关键词:
天空盒
或
天空穹顶
提供了有效且相对简单的方法,用来生成令人信服的地平线景观。
天空盒
- 如何为地平线制作纹理?
立方体有 6 个面,我们需要为这些面都添加纹理。
一种方法是使用 6 个图像文件和 6 个纹理单元。
另一种常见(且高效)的方式则是使用一个包含 6 个面的纹理的图像。
使用纹理立方体贴图为立方体添加纹理需要指定适当的纹理坐标。下图展示了纹理坐
标的分布,这些坐标接着会分配给立方体的每个顶点。
- 如何让天空盒看起来“距离很远”?
通过使用以下两个技巧,可以使天空盒显得巨大(从而感觉距离很远):
(a)禁用深度测试并先渲染天空盒(在渲染场景中的其他对象时重新启用深度测试);
(b)天空盒随相机移动(如果相机需要移动)。
通过在禁用深度测试的情况下先绘制天空盒,深度缓冲器的值仍将全设为 1.0(即最远
距离)。因此,场景中的所有其他对象将被完全渲染,即天空盒不会阻挡任何其他对象。这
样,无论天空盒的实际大小如何,会使天空盒的各面的位置看起来比其他物体都更远。而
实际的天空盒立方体本身可以非常小,只要它在相机移动时随相机一起移动即可。下图展示了从天空盒内部查看简单的场景。
场景中可见的天空盒部分是立方体贴图的最右侧部分。这是因为摄像机处于默认方向,面向−Z 方向,因此正在观察天空盒立方体的背面(如“立方体贴图纹理坐标”图所示)。另请注意,立方体贴图的背面在场景中渲染时会呈水平反转状态;这是因为立方体贴图的“背面”部分已经折叠在相机周围,因此看起来是经过侧向翻转的(如文章顶部两图所示)。 - 如何构建纹理立方体贴图?
从图稿或照片构建纹理立方体贴图图像时,需要注意避免在立方体面交汇点处的“接缝”,并创建正确的透视图,才能让天空盒看起来逼真且无畸变。有许多工具可以辅助达成这一目标: Terragen、 Autodesk 3Ds Max、 Blender 和 Adobe Photoshop 都有用于构建或处理立方体贴图的工具。同时,还有许多网站提供各种现成的立方体地图。
天空穹顶
建立地平线效果的另一种方法是使用天空穹顶
。除了使用带纹理的球体(或半球体)代替带纹理的立方体,其基本思路与天空盒相同。与天空盒相同,我们首先渲染天空穹顶(禁用深度测试),并将摄像机保持在天空穹顶的中心位置(下图中的天空穹顶纹理是使用Terragen这个工具制作的)。
天空穹顶相比天空盒有自己的优势。例如,它们不易受到畸变和接缝的影响(尽管在纹理图像中必须考虑极点处的球形畸变)。
天空穹顶的缺点之一则是球体或穹顶模型比立方体模型更复杂,天空穹顶有更多的顶点,其数量取决于期望的精度。
当使用天空穹顶呈现室外场景时,通常与地平面或某种地形相结合。当使用天空穹顶呈现宇宙中的场景(例如星空)时,使用下图所示的球体通常更为实际(为了清晰地使球体可视化,球体表面添加了一道虚线)。
实现天空盒
尽管天空穹顶有许多优点,天空盒仍然更为常见。 OpenGL 对天空盒的支持也更好,在进行环境贴图时更方便(本章后面会介绍)。出于这些原因,我们将专注于天空盒的实现。天空盒有两种实现方法:从头开始构建一个简单的天空盒;或使用 OpenGL 中的立方体贴图工具。它们有各自的优点。
从头开始构建天空盒
这里,我们将看到如何简单地启用和禁用深度测试(只需要一行代码)。场景中仅包含一个带纹理的环面。
//所有变量声明,构造函数和 init()与之前相同
. . .
void display(GLFWwindow* window, double currentTime)
// 清除颜色缓冲区和深度缓冲区,并像之前一样创建投影视图矩阵和摄像机视图矩阵
. . .
vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));
glUseProgram(renderingProgram);
// 准备首先绘制天空盒。 M 矩阵将天空盒放置在摄像机位置
// 注:mMat当然不是负相机位置,因为相机在世界空间中位置就是(cameraX,cameraY,cameraZ)
mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cameraX, cameraY, cameraZ));
// 构建 MODEL-VIEW 矩阵
mvMat = vMat * mMat;
// 如前,将 MV 和 PROJ 矩阵放入统一变量
. . .
// 设置包含顶点的缓冲区
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
// 设置包含纹理坐标的缓冲区
glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
//激活天空盒纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, skyboxTexture);
glEnable(GL_CULL_FACE);
// 立方体缠绕顺序是顺时针的,但我们从内部查看,因此使用逆时针缠绕顺序:GL_CCW
glFrontFace(GL_CCW);
// 在[没有深度测试的情况下]绘制天空盒
glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_DEPTH_TEST);
//现在像之前一样绘制场景中的对象
. . .
glDrawElements( . . . ); //和之前的场景中的对象一样
void setupVertices(void)
// cube_vertices 定义与之前相同
// 天空盒的立方体纹理坐标,如图“立方体贴图纹理坐标”所示
float cubeTextureCoord[72] =
1.00f, 0.66f, 1.00f, 0.33f, 0.75f, 0.33f, // 背面右下角
0.75f, 0.33f, 0.75f, 0.66f, 1.00f, 0.66f, // 背面左上角
0.75f, 0.33f, 0.50f, 0.33f, 0.75f, 0.66f, // 右面右下角
0.50f, 0.33f, 0.50f, 0.66f, 0.75f, 0.66f, // 右面左上角
0.50f, 0.33f, 0.25f, 0.33f, 0.50f, 0.66f, // 正面右下角
0.25f, 0.33f, 0.25f, 0.66f, 0.50f, 0.66f, // 正面左上角
0.25f, 0.33f, 0.00f, 0.33f, 0.25f, 0.66f, // 左面右下角
0.00f, 0.33f, 0.00f, 0.66f, 0.25f, 0.66f, // 左面左上角
0.25f, 0.33f, 0.50f, 0.33f, 0.50f, 0.00f, // 下面右下角
0.50f, 0.00f, 0.25f, 0.00f, 0.25f, 0.33f, // 下面左上角
0.25f, 1.00f, 0.50f, 1.00f, 0.50f, 0.66f, // 上面右下角
0.50f, 0.66f, 0.25f, 0.66f, 0.25f, 1.00f // 上面左上角
;
//像往常一样为立方体和场景对象设置缓冲区
//用于加载着色器、纹理、 main()等的模块,如前
给的天空盒图片如下:
程序运行效果如下:
天空盒容易受到图像畸变和接缝的影响。 接缝指两个纹理图像接触的地方(比
如沿着立方体的边缘)有时出现的可见线条。下图展示了一个图像上半部分出现接缝的示例,它是运行上面程序时出现的伪影。为了避免接缝,需要仔细构建立方体贴图图像,并分配精确的纹理坐标。有一些工具可以用来沿图像边缘减少接缝(例如GNU Image Manipulation Program)。
使用OpenGL立方体贴图
构建天空盒的另一种方法是使用 OpenGL纹理立方体贴图
。 OpenGL 立方体贴图比我们在上面看到的简单方法稍微复杂一点。但是,使用 OpenGL 立方体贴图有自己的优点,例如减少接缝以及支持环境贴图。
OpenGL 纹理立方体贴图类似于稍后将要研究的 3D 纹理,它们都使用 3 个纹理坐标访问——通常标记为(s, t, r)。
OpenGL 纹理立方体贴图其中的图像以纹理图像的【左上角】为(0, 0, 0)。
上面程序通过读入单个图像来为立方体贴图添加纹理,而这里将用loadCubeMap()函数读入6个单独的立方体面图像文件。
在这里,SOIL2用于实例化和加载OpenGL立方体贴图也非常方便。
在使用OpenGL立方体贴图时,无须垂直翻转纹理,OpenGL会自动处理。
init()函数现在包含一个函数调用以启用 GL_TEXTURE_CUBE_MAP_SEAMLESS, 它告诉 OpenGL 尝试混合立方体相邻的边以减少或消除接缝。
在 display()中,立方体的顶点像以前一样沿管线向下发送,但这次不需要发送立方体的纹理坐标。我们将会看到, OpenGL 纹理立方体贴图通常使用立方体的顶点位置作为其纹理坐标。之后禁用深度测试并绘制立方体。然后为场景的其余部分重新启用深度测试。
完成后的 OpenGL 纹理立方体贴图使用了 int 类型的标识符进行引用。与阴影贴图时一样,通过将纹理包裹模式设置为“夹紧到边缘”,可以减少沿边框的伪影。在这种情况下,它还可以帮助进一步缩小接缝。请注意,这里需要为 3 个纹理坐标 s、 t 和 r 都设置纹理包裹模式。
在片段着色器中使用名为 samplerCube
的特殊类型的采样器访问纹理。在纹理立方体贴图中,从采样器返回的值是沿着方向向量(s, t, r)从原点“看到”的纹素。因此,我们通常可以简单地使用传入的插值顶点位置作为纹理坐标。在顶点着色器中,我们将立方体顶点位置分配到输出纹理坐标属性中,以便在它们到达片段着色器时进行插值。另外需要注意,在顶点着色器中,我们将传入的视图矩阵转换为 3× 3,然后再转换回 4× 4。这个“技巧”有效地移除了平移分量,同时保留了旋转。这样,就将立方体贴图固定在了摄像机位置,同时仍允许合成相机“环顾四周”。
分析矩阵中值的存储 及 mat4转mat3原理
如下分别为平移矩阵、缩放矩阵和绕轴旋转的变换矩阵:
(
平
移
变
换
)
(
X
+
T
x
Y
+
T
y
Z
+
T
z
1
)
=
[
1
0
0
T
x
0
1
0
T
y
0
0
1
T
z
0
0
0
1
]
×
(
X
Y
Z
1
)
(
缩
放
变
换
)
(
X
∗
S
x
Y
∗
S
y
Z
∗
S
z
1
)
=
[
S
x
0
0
0
0
S
y
0
0
0
0
S
z
0
0
0
0
1
]
×
(
X
Y
Z
1
)
(
绕
X
轴
旋
转
)
(
X
′
Y
′
Z
′
1
)
=
[
1
0
0
0
0
c
o
s
θ
−
s
i
n
θ
0
0
s
i
n
θ
c
o
s
θ
0
0
0
0
1
]
×
(
X
Y
Z
1
)
(
绕
Y
轴
旋
转
)
(
X
′
Y
′
Z
′
1
)
=
[
c
o
s
θ
0
s
i
n
θ
0
0
1
0
0
−
s
i
n
θ
0
c
o
s
θ
0
0
0
0
1
]
×
(
X
Y
Z
1
)
(
绕
Z
轴
旋
转
)
(
X
′
Y
′
Z
′
1
)
=
[
c
o
s
θ
−
s
i
n
θ
0
0
s
i
n
θ
c
o
s
θ
0
0
0
0
1
0
0
0
0
1
]
×
(
X
Y
Z
1
)
(平移变换) \\left( \\beginarray l X+T_x \\\\ Y+T_y \\\\ Z+T_z \\\\ 1 \\endarray \\right) = \\left[ \\beginarray l l l l 1 & 0 & 0 & T_x \\\\ 0 & 1 & 0 & T_y \\\\ 0 & 0 & 1 & T_z \\\\ 0 & 0 & 0 & 1 \\endarray \\right] \\times \\left( \\beginarray l X \\\\ Y \\\\ Z \\\\ 1 \\endarray \\right)\\\\(缩放变换) \\left( \\beginarray l X*S_x \\\\ Y*S_y \\\\ Z*S_z \\\\ 1 \\endarray \\right) = \\left[ \\beginarray l l l l S_x & 0 & 0 & 0 \\\\ 0 & S_y & 0 & 0 \\\\ 0 & 0 & S_z & 0 \\\\ 0 & 0 & 0 & 1 \\endarray \\right] \\times \\left( \\beginarray l X \\\\ Y \\\\ Z \\\\ 1 \\endarray \\right)\\\\(绕X轴旋转) \\left( \\beginarray l X' \\\\ Y' \\\\ Z' \\\\ 1 \\endarray \\right) = \\left[ \\beginarray l l l l 1 & 0 & 0 & 0 \\\\ 0 & cosθ & -sinθ & 0 \\\\ 0 & sinθ & cosθ & 0 \\\\ 0 & 0 & 0 & 1 \\endarray \\right] \\times \\left( \\beginarray l X \\\\ Y \\\\ Z \\\\ 1 \\endarray \\right)\\\\(绕Y轴旋转) \\left( \\beginarray l X' \\\\ Y' \\\\ Z' \\\\ 1 \\endarray \\right) = \\left[ \\beginarray l l l l cosθ & 0 & sinθ & 0 \\\\ 0 & 1 & 0 & 0 \\\\ -sinθ & 0 & cosθ & 0 \\\\ 0 & 0 & 0 & 1 \\endarray \\right] \\times \\left( \\beginarray l X \\\\ Y \\\\ Z \\\\ 1 \\endarray \\right)\\\\(绕Z轴旋转) \\left( \\beginarray l X' \\\\ Y' \\\\ Z' \\\\ 1 \\endarray \\right) = \\left[ \\beginarray l l l l cosθ & -sinθ & 0 & 0 \\\\ sinθ & cosθ & 0 & 0 \\\\ 0 & 0 & 1 & 0 \\\\ 0 & 0 & 0 & 1 \\endarray \\right] \\times \\left( \\beginarray l X \\\\ Y \\\\ Z \\\\ 1 \\endarray \\right)
(平移变换)⎝⎜⎜⎛X+TxY+TyZ+Tz1⎠⎟⎟⎞=⎣⎢⎢⎡100001000010TxTyTz1⎦⎥⎥⎤×⎝⎜⎜⎛查看详情
p1195口袋的天空(代码片段)
...背景小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。题目描述给你云朵的个数N,再给你M个关系,表示哪些云朵可以连在一起。现在小杉... 查看详情
inavflight之mspdji协议天空端请求报文(代码片段)
iNavFlight之MSPDJI协议天空端请求报文1.报文格式(请求)2.报文标志(flag)3.报文命令(cmd)4.参考资料MSPDJI协议是用于DJI天空端与飞控端之间的通信协议,其工作模式符合C/S经典设计。这里我们重点介绍下天空端请求报文格式和命令。... 查看详情
小a和uim之大逃离(代码片段)
...道道闪电,一阵阵雷声。刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个披头散发、青面獠牙的怪物,低沉着声音说:“呵呵,既然你们来到这,只能活下来一个!”。小a和他... 查看详情
sh天空漏斗(代码片段)
p1373小a和uim之大逃离(代码片段)
...道道闪电,一阵阵雷声。刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个披头散发、青面獠牙的怪物,低沉着声音说:“呵呵,既然你们来到这,只能活下来一个!”。小a... 查看详情
[p1373]小a和uim之大逃离(代码片段)
...道道闪电,一阵阵雷声。刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个披头散发、青面獠牙的怪物,低沉着声音说:“呵呵,既然你们来到这,只能活下来一个!”。小a... 查看详情
猿创征文|程序猿乘风破浪pythonpygame原创小游戏源码+解析(代码片段)
...别为: (一)游戏背景 1、天空背景。用多条蓝色横线点缀的白色天空,使用Python代码让天空背景从右到左不停的移动,从而制造猴子向右移动的假象。天空背景背景精灵代码:classBackgroun... 查看详情
猿创征文|程序猿乘风破浪pythonpygame原创小游戏源码+解析(代码片段)
...别为: (一)游戏背景 1、天空背景。用多条蓝色横线点缀的白色天空,使用Python代码让天空背景从右到左不停的移动,从而制造猴子向右移动的假象。天空背景背景精灵代码:classBackgroun... 查看详情
luogup1195口袋的天空(代码片段)
...题是学长推荐做的(我点名要水题/*超喜欢题面嗷漂亮的天空上有漂亮的云彩,漂亮的孩子做着漂亮的梦(而且立刻联想到小云彩嗷嗷嗷 qwq*/无视以上废话,看题其实和最小生成树的模版题没什么区别(悄咪咪表示某谷测试... 查看详情
小功能⭐️unity动态更换天空盒旋转天空盒(代码片段)
文章目录🟥Unity动态更换天空盒1️⃣方法12️⃣方法2🟧旋转天空盒🟥Unity动态更换天空盒1️⃣方法11、在摄像头上添加SkyBox组件放到其他地方不管用。2、创建SkyBox类型的的材质球。放入即可。3、通过代码,你便... 查看详情
观影目录(代码片段)
x记忆大师 青春派(从居然承认他失恋的那时候,《我的天空》作为背景音乐响起的时候,) 前任3 x 查看详情
opencv-实现天空变换(图像分割)(代码片段)
...作者获得授权,非商业转载请注明出处实现原理 天空变换是图像分割的一种应用,把图像中的天空与非天空区分割开,结合掩膜将天空更改为其他图像。如何较优地实现天空变换,与识别证件照类似,难... 查看详情
cesium之天空盒对应方位(代码片段)
下文讲解一下关于Cesium的天空盒具体方位。天空盒对应图一个立方体展开图,相当于一个站在negz的位置,背对电脑屏幕,对应关系如下negz→downposx→rightnegx→leftposy→backposz→upnegy→front分享一个天空盒网址www.custommapmakers.o... 查看详情
天空卫士c++一面(技术面61min)(代码片段)
天空卫士C++一面(技术面、61min)面试官问你现在是在做毕设吗?这学期在干什么呢?自我介绍~项目是老师带着做的还是怎么一回事呢?你是哪里人呢?看你得的奖挺多的你们班有多少人进实验室C跟... 查看详情
天空卫士c++一面(技术面61min)(代码片段)
天空卫士C++一面(技术面、61min)面试官问你现在是在做毕设吗?这学期在干什么呢?自我介绍~项目是老师带着做的还是怎么一回事呢?你是哪里人呢?看你得的奖挺多的你们班有多少人进实验室C跟... 查看详情
androidopengles2.0(十七)——球形天空盒vr效果实现(代码片段)
在3D游戏中通常都会用到天空盒,在3D引擎中也一般会存在天空盒组件,让开发者可以直接使用。那么天空盒是什么?天空盒又是如何实现的呢?本篇博客主要介绍如何在Android中利用OpenGLES绘制一个天空盒,并... 查看详情
unity3d灵巧小知识点☀️|unity中使用代码切换天空盒(代码片段)
Unity小科普老规矩,先介绍一下Unity的科普小知识:Unity是实时3D互动内容创作和运营平台。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助Unity将创意变成现实。Unity平台提供一整套完善的软件解... 查看详情