Android OpenGL光照效果

在计算机图形学领域,光照仿真是一个重要的研究领域,它对游戏画面的提升、电影和电视节目中的电脑生成图像(CGI)等方面产生了显著影响。通过使用不同的光照算法,我们可以改变场景的外观,例如模拟从白天到夜晚的过渡,或者在山体上产生山峰、峡谷和裂隙的效果。即使是二维场景,也可以通过光照特性来创造出视觉深度或爆炸效果。

在本章节中,我们将探索如何利用光照算法来增强山体的视觉效果。首先,我们会学习使用方向光源来实现漫反射,这会使得场景看起来像是被天空中的太阳照射。接着,我们将添加环境光以减少阴影区域的黑暗程度。

之后,我们将关闭天空盒,使其变暗,并学习如何使用点光源来照亮每个粒子喷泉。为了开始这个项目,我们将继续在上一章节的项目开始。

仿真光照效果

我们所看到的世界,实际上是由无数微小的光子累积而成的效果。这些光子从光源,比如太阳发射出来,经过长距离的旅行后,会从物体上反射或折射,最终到达我们的眼睛。眼睛和大脑接收这些光子,并重建它们形成的图像,这就是我们所看到的世界。

在计算机图形学中,模拟光子的行为来仿真光照效果是一项挑战。一种方法是使用射线跟踪器,它通过发射射线来模拟光子,并计算射线与场景中物体的交互,从而实现反射、折射和焦散等效果。尽管射线跟踪器非常强大,但它在实时渲染中的计算成本过高。

因此,大多数游戏和应用程序采用简化的方法来近似模拟光线的行为,而不是直接模拟光子。这些简化的算法广泛使用,并且有多种方法可以模拟反射、折射等效果。这些技术能够将大部分计算负载分配给GPU,即使在移动设备上也能快速运行。

在OpenGL中使用光照

在OpenGL中,我们可以通过不同类型的光源来为场景添加光照效果。这些光源包括:

环境光:它似乎从各个方向均匀地照亮场景,类似于天空的光照。环境光有助于避免阴影区域变得完全黑暗。

方向光:这种光看起来来自一个特定的方向,就像太阳或月亮的光照,光源似乎非常遥远。

点光:点光从场景中的一个点发出,随着距离的增加,光照强度逐渐减弱,类似于灯泡或蜡烛的照明效果。

聚光:与点光相似,但聚光只向一个特定方向投射,类似于手电筒或舞台聚光灯的效果。

此外,光线在物体表面的反射方式也可以分为两种:

漫反射:光线均匀地向各个方向散射,适合模拟没有光泽的表面,如地毯或混凝土墙。

镜面反射:光线在特定方向上强烈反射,适合模拟光滑或闪亮的材质,如金属或打过蜡的汽车表面。

许多材质同时具有这两种反射特性。例如,沥青路面通常看起来在各个方向上都是一样的,但在某些条件下,如太阳低空时,可能会在特定方向上产生强烈的反射,这有时会导致司机视线受阻,甚至引发交通事故。

博朗反射实现方向光

为了在图形渲染中实现漫反射效果,我们可以使用一种称为朗伯体反射的技术。这种技术以18世纪的瑞士数学家和天文学家Johann Heinrich Lambert的名字命名。朗伯体反射描述了一种表面,无论光线从哪个方向照射,它都能均匀地反射光线,使得从任何观察点看,表面看起来都是一样的。这种反射效果仅取决于表面与光源的相对位置和距离。

以一个简单的例子来说明:假设有一个水平表面和一盏方向光源,光源的强度不随距离减弱。在这种情况下,影响反射的唯一因素是表面相对于光源的方向。如下图所示,如果表面垂直于光源,它将捕捉并反射最多的光线。而在下图中,如果表面相对于光源旋转45度,它捕捉和反射的光线就会减少。

具体来说,当表面旋转45度时,它反射的光线量会减少到原来的0.707倍,这与旋转角度的余弦值有关。要计算表面接收到的光线量,我们只需计算出它直接面向光源时接收的光线量,然后乘以该角度的余弦值。

例如,如果一个朗伯体表面在与光源成0度角时接收了5流明的光线,那么当它与光源成45度角时,它将反射大约3.5流明的光线(5 × cos 45°)。理解朗伯体反射的关键在于理解这种光线接收量与角度余弦值之间的关系。

1.计算高度图的方位

在为高度图添加朗伯体反射效果之前,我们需要一种方法来确定其表面的朝向。由于高度图不是完全水平的,我们需要计算高度图上每个点的朝向。我们可以通过表面法线来表示这个朝向,这是一种特殊的向量,它垂直于表面且长度为1。

在计算每个点的法线时,我们实际上是将该点周围的相邻点结合起来,形成一个平面。我们用两个向量来定义这个平面:一个向量从右侧的点指向左侧的点,另一个向量从上面的点指向下面的点。通过计算这两个向量的叉积,我们可以得到一个垂直于该平面的向量,这就是我们所说的表面法线。然后,我们将这个向量归一化,使其长度为1,从而得到中间点的表面法线。

简而言之,通过计算高度图上每个点周围相邻点形成的平面的法线,我们能够确定每个点的表面朝向,这对于实现朗伯体反射效果至关重要。

我们来看看下图的一个例子:

在处理高度图时,我们通常假设每个点占据一个单位立方体,其中x坐标向右增加,z坐标向下增加。为了计算表面法线,我们需要考虑每个点周围的相邻点。

计算步骤
确定相邻点的高度:假设一个点的上边、左边、右边和下边的点的高度分别是0.2、0.1、0.1和0.1。

计算向量

  • 从右向左的向量:用右边的点的高度减去左边的点的高度,得到向量(-2, 0, 0)。
  • 从上向下的向量:用上边的点的高度减下去边的点的高度,得到向量(0, -0.1, 2)。

生成表面法线

  • 计算叉积:将这两个向量进行叉积运算,得到向量(0, 4, 0.2)。
  • 归一化:将得到的向量归一化,得到表面法线(0, 0.9988, 0.05)。

选择向量方向的原因
右手规则:我们使用从右向左的向量,因为我们希望表面法线指向上方,离开高度图。通过使用右手规则,我们可以确保叉积的方向是正确的。

这种方法确保了我们能够准确地计算出高度图中每个点的表面法线,这对于后续的光照计算和渲染效果至关重要。

现在我们已经知道了要做什么,让我们打开HeightMap类,开始改写代码。首先,加入一些新的常量:

    private val POSITION_COMPONENT_COUNT = 3
    private val NORMAL_COMPONENT_COUNT = 3
    private val TOTAL_COMPONENT_COUNT = POSITION_COMPONENT_COUNT + NORMAL_COMPONENT_COUNT
    private val STRIDE = (POSITION_COMPONENT_COUNT+NORMAL_COMPONENT_COUNT)*BYTES_PER_FLOAT

我们将改变顶点缓冲区,以使它存储位置和法线,为此,我们需要知道全部的分量计数和跨距。让我们更新loadBitmapData()中heightmapVertices的赋值语句,为法线增加一些空间:

 val heightmapVertices = FloatArray(width*height*TOTAL_COMPONENT_COUNT)

这可以保证有足够的空间留给位置和法线。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值