OGL(教程17)——环境光

原文地址:http://ogldev.atspace.co.uk/www/tutorial17/tutorial17.html

背景知识:
光照在3D图形学中很重要。如果把光照加入到模型渲染中会增加场景的逼真程度。使用模拟,是因为它不能100%模拟现实世界。现实的光,是由大量的粒子称之为光子构成的,表现出粒子特性和波特性。如果你试图计算每个光子的影响,你的计算机无法做到这个。

因此,几个光照模型被开发出来,他们模拟光的核心效果,当光照照射到物体的时候。光照模型跟着计算机的升级而变得越来越复杂。在下一节教程开始,我们将会学习基础的光照模型,他们很简答。但是对于所有的大气层中的场景都影响很大。

最基本的光照模型是Ambient/Diffuse/Specular。环境光是光照的一种类型,当你走出室内你就会看到环境光。即使是太阳落下了,它的光射线撞击到不同的物体,不同的物体就会被看到,即使是在影子里。由于光在每个它撞击到物体之间进行多次反射,他不是一条笔直的路径。甚一个灯泡在室内,看起来像是太阳的感觉。把它的环境的光向四周发射,如果房间足够大,每个东西都是均等的。环境光模拟的是没有起点、没有方向、对每个物体都有相同效果。

漫反射重点强调的是,光是从哪个角度照射到物体表面的,强调的是照射物体的亮度。当光和物体的一个面撞击,那么此面就比另外一个面亮一些。我们仅仅看到太阳传播环境光,没有特定的方向。但是,太阳也有漫反射属性。当他撞击到高的建筑物是,你会看到建筑物的一面比另外一面亮。漫反射光的最重要的属性是方向。

镜面光,是物体的另外一个属性,而不是光本身的。它使得物体某个部分看起来特别刺眼。这个还和视角的方向有关。金属物体经常有一些反射属性,比如一辆车在晴天会有边缘刺眼的效果。计算镜面反射的时候要考虑光的入射方向,以及观察者的观察方向。

在3D应用中,你清楚不需要创建环境光、漫反射、镜面反射。反而,你使用光源,比如户外的太阳,室内的灯泡,或者是岩洞中的聚光灯。这些光源类型会把环境光、漫反射、镜面反射的效果结合起来。比如,聚光灯,能够照射特定范围的物体,在范围之外的则不会照亮。

在接下来的教程中,我们开发几种有用的光源类型,学习基本的光照模型。

我们从平行光开始。一个平行光有方向,但是没有起点。这就意味着,所有的光射线都是平行的。光的方向用一个向量表示,所有的物体都是使用这个向量计算光,而不考虑起点位置。太阳就是平行光。如果你试图计算两个相邻的建筑和太阳光的夹角的话,两个角度是几乎相同的。因为太阳在150百万千米之外。因此,我们只考虑其方向。

平行光的另外一个重要的属性是,它的亮度和距离无关。这个亮度是一个常量值。而点光源,则是距离越远,其亮度越低。

下图展示了平行光:
在这里插入图片描述

我们已经看到太阳既有环境光+漫反射属性。本节将会开发环境光部分,下一节开发漫反射部分。

前一节中我们介绍了如何从一个贴图中采样颜色。颜色分为三个通道(RGB),每个通道一个字节。那么意味着,颜色范围在0到255之间。不同的颜色通道结合起来,就得到不同的颜色。都为0则为黑色,都为255则为白色。其他的东西都在黑白之间。可以对分量做等比缩放,其亮度根据缩放值变暗或者变亮。

当白光撞击到表面,表面就会反射白色。这个亮度或明或暗,取决于光源的亮度。但是反射都是同样的基础色。如果光源是纯红(255,0,0),那么反射的颜色只能是红色。这是因为光源没有绿色和蓝色用来反射。如果物体表面是纯蓝,那么结果是黑色。基础的原则是,光源只能反射物体的颜色,但是不能对其着色。

我们定义光源的颜色在[0,1]之间。把这个值乘以物体的颜色,就得到了反射的颜色。但是,我们需要考虑环境光强度。因此,环境光强度会被单做一个因子,用来乘以每个分量。这个就是最终颜色。等式如下:
在这里插入图片描述

在本节的教程中,你可以按住a和s键来增加和减少环境光的强度。这个只是平行光的环境光部分。这个在下一节的漫反射中会有所变化。目前,我们从任何角度看物体都是一样的。

环境光的效果大多不被使用。因为,它看起来很假。由于其实现过于简单,对写实性贡献不大。使用高级的方法,例如全局光照,可以避免使用单一的环境光的需求,由于灯光被物体反射然后又撞击到其他物体,所有这些情况都被考虑进去了。有些时候还是需要些环境光的,比如避免某个物体的单一的面完全黑色,这时候需要一点环境光。所以我们还是要提供一个参数用以微调环境光的强度。

代码注释:

我们的代码例子,变得越来越复杂,并且这种趋势会越来越明显。本节,处理实现环境光,我们还要做的工作就是重构代码。我们提前把代码归置好,最大的变换如下:

  1. 在Technique类中,封装了shader的管理方法。这些活动包括编译、链接。从现在开始我们将会在继成自Technique类中实现我们的可视化效果。
  2. 把GLUT的初始和回调管理移动到GLUTBackend组件中。这个组件自身复杂注册和响应回调函数,然后派发给C++接口提供所谓的ICallacks。
  3. 把main文件中全局的函数和变量移动到一个类中,此类可以叫做应用。在将来,我们将会针对不同的应用扩展这个基类,它将会提供更多的功能。这个在很多引擎和框架中经常使用。

本节的大多数代码,除了灯光的代码,不是新的,只是重新组织了下。因此新的头文件需要重新定义下。

(glut_backend.h:24)

void GLUTBackendInit(int argc, char** argv);

bool GLUTBackendCreateWindow(unsigned int Width, unsigned int Height, unsigned int bpp, bool isFullScreen, const char* pTitle);

GLUT的特定的代码大多数移动到GLUT backend组件中,这个是GLUT很方便得到初始化,并且创建一个窗口。


(glut_backend.h:28)

void GLUTBackendRun(ICallbacks* pCallbacks);

在GLUT被初始化之后,在主循环中调用上面的函数,窗口就被创建出来了。这里有一个新增加的ICallbacks接口,这个用于注册GLUT回调函数。……

(technique.h:25)

class Technique
{
public:

   Technique();

   ~Technique();

   virtual bool Init();

   void Enable();

protected:

   bool AddShader(GLenum ShaderType, const char* pShaderText);

   bool Finalize();

   GLint GetUniformLocation(const char* pUniformName);

private:

   GLuint m_shaderProg;

   typedef std::list<GLuint> ShaderObjList;
   ShaderObjList m_shaderObjList;
};

之前的章节中,所有的编译和链接shader的工作都是应用程序的部分处理工作。这里的Technique类负责把常用的函数封装到自身,然后让其子类负责核心的特效工作。

每个technique对象都要首先初始化,调用的函数是Init()。technique子类必须调用基类的Init()方法。

当Technique对象被创建和初始化之后,最常规的子类操作顺序是AddShader(),比如添加下GLSL shader脚本,然后是调用Finalize()用来连接shader。Enable()方法是glUseProgram的包装,所以当调用绘制函数的时候,它就要被调用。

这个类跟踪了编译过程中的中间对象,在链接之后,调用glDeleteShader()方法,删除中间对象。这个有助于减少应用的使用资源。为了更好的性能,OpenGL应用经常是在加载的时候编译shader,而不是在运行的时候编译。在链接之后立即删除对象,可以保证OpenGL资源利用很低。程序对象本身使用glDeleteProgram()函数,其内部调用析构函数。

(tutorial17.cpp:49)

class Tutorial17 : public ICallbacks
{
public:

 	 	Tutorial17()
 	 	{
 	 	 	 	...
 	 	}

 	 	~Tutorial17()
 	 	{
 	 	 	 	...
 	 	}

 	 	bool Init()
 	 	{
 	 	 	 	...
 	 	}

 	 	void Run()
 	 	{
 	 	 	 GLUTBackendRun(this);
 	 	}

 	 	virtual void RenderSceneCB()
 	 	{
 	 	 	 	...
 	 	}

 	 	virtual void IdleCB()
 	 	{
 	 	 	 	...
 	 	}

 	 	virtual void SpecialKeyboardCB(int Key, int x, int y)
 	 	{
 	 	 	 	...
 	 	}

 	 	virtual void KeyboardCB(unsigned char Key, int x, int y)
 	 	{
 	 	 	 	...
 	 	}

 	 	virtual void PassiveMouseCB(int x, int y)
 	 	{
 	 	 	 	...
 	 	}

private:

 	 	void CreateVertexBuffer()
 	 	{
 	 	 	 	...
 	 	}
 	 	void CreateIndexBuffer()
 	 	{
 	 	 	 	...
 	 	}

 	 	GLuint m_VBO;
 	 	GLuint m_IBO;
 	 	LightingTechnique* m_pEffect;
 	 	Texture* m_pTexture;
 	 	Camera* m_pGameCamera;
 	 	float m_scale;
 	 	DirectionalLight m_directionalLight;
};
static const char* pVS = "                                                          \n\
#version 330                                                                        \n\
                                                                                    \n\
layout (location = 0) in vec3 Position;                                             \n\
layout (location = 1) in vec2 TexCoord;                                             \n\
                                                                                    \n\
uniform mat4 gWVP;                                                                  \n\
                                                                                    \n\
out vec2 TexCoord0;                                                                 \n\
                                                                                    \n\
void main()                                                                         \n\
{                                                                                   \n\
    gl_Position = gWVP * vec4(Position, 1.0);                                       \n\
    TexCoord0 = TexCoord;                                                           \n\
}";

static const char* pFS = "                                                          \n\
#version 330                                                                        \n\
                                                                                    \n\
in vec2 TexCoord0;                                                                  \n\
                                                                                    \n\
out vec4 FragColor;                                                                 \n\
                                                                                    \n\
struct DirectionalLight                                                             \n\
{                                                                                   \n\
    vec3 Color;                                                                     \n\
    float AmbientIntensity;                                                         \n\
};                                                                                  \n\
                                                                                    \n\
uniform DirectionalLight gDirectionalLight;                                         \n\
uniform sampler2D gSampler;                                                         \n\
                                                                                    \n\
void main()                                                                         \n\
{                                                                                   \n\
    FragColor = texture2D(gSampler, TexCoord0.xy) *                                 \n\
                vec4(gDirectionalLight.Color, 1.0f) *                               \n\
                gDirectionalLight.AmbientIntensity;                                 \n\
}";

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值