计算机图形学笔记六:Shading 1(光照和三种反射)

1.认识着色(Shading)

概念表示:通过平行线或色块使插图或图表变暗或上色,对图形学来说,就是对物体采用材质的过程。着色操作之后显得更加真实

2.Blinn - Phong着色模型

它是一个经验模型,并不完全符合真实世界中的光照现象。这种模型将看到的光线分为三部分高光、漫反射、环境光照
在这里插入图片描述

3.着色点的光照

在这里插入图片描述

  • 观察者的方向向量(Viewer direction)v
  • 着色点所在表面的法向量(Surface normal)n
  • 光线照射的方向向量(Light direction)l
  • 着色点表面的一些参数(颜色,材质,反光度等)
  • 三个向量均为单位向量

接着我们对Blinn - Phong着色模型中的三种光进行分析。

4.漫反射(Diffuse Reflection)

4.1漫反射原理

概念表示: 一束光打在物体的某个点上,光线会均匀的反射到各个不同的方向上去。
注:漫反射与观察者的位置没有关系。
在这里插入图片描述
如上图所示,当一束光漫照射到某个物体表面时,漫反射是向四面八方均匀的反射光纤,所以观察者不管从什么角度观察,表面的颜色都是一样的。

4.2物体接受光线能量的方式

但是同样的光以不同的角度照在同样的表面上,得到的明暗程度是不一样的。这是什么原因呢?
在这里插入图片描述
如上图所示,可以看出当面斜着的时候,单位面积接收到光的能量更小。并且,影响漫反射时的亮度不同的因素是“光与面的角度,而与视角角度无关”。
计算从不同角度接受到的光线能量有一个Lambert’s cosine law(兰伯特余弦定理):可以通过光线照射的方向向量(i)和接收面的法线的向量(n)的夹角,来计算一个面的亮度。接收到的光线的能量和 光线方向与Shading point的角度的cos值成正比。

4.3光衰减(Light Falloff)

在这里插入图片描述
如上图所示,假设在半径为 r = 1 r=1 r=1的这个球壳上所蕴含的能量为 k k k,而在这个球壳上的每一点的光的强度是 I I I,那么在半径为 r r r的球壳上,在这个球壳上的光线的强度就应该是 I / r 2 I/r^2 I/r2。又因为光线在传播过程中没有能量损失,结合能量守恒定律,那么在每一个球壳上的能量的大小应该是相同的。
所以距离越远,光照的强度越弱。

4.4漫反射强度计算( L d L_d Ld

在这里插入图片描述
L d = K d ( I / r 2 ) m a x ( 0 , n ⋅ l ) L_d=K_d(I/r^2)max(0,n·l) Ld=Kd(I/r2)max(0,nl)

  • L d L_d Ld:漫反射光
  • K d K_d Kd:漫反射系数(颜色),即表示这个点吸收光的系数,若 K d = 1 K_d=1 Kd=1则表示该点完全不吸收光,若 K d = 0 K_d=0 Kd=0,则无光反射出去,若表示为一个RGB的值,则会给这个点表现为某种颜色。
  • ( I / r 2 ) (I/r^2) (I/r2):光能量到达着色点处
  • m a x ( 0 , n ⋅ l ) max(0,n·l) max(0,nl):在着色点接收的能量

漫反射光的具体计算过程。从公式也能看出视角并不影响漫反射光强。
K d K_d Kd变大示例图:
在这里插入图片描述

5.镜面反射(Specular Term)

镜面反射又叫高光。

5.1镜面反射原理

概念表示: 将光照照向镜面一点,此时通过入射向量 v v v,法线向量 n n n,我们可以求出此时在镜面上唯一的反射向量 R R R,我们称其为镜面反射.
在这里插入图片描述
如上图可知,当我们的观察方向V与反射方向R接近时,我们可以看见高光。
当观察方向和光的反射方向越近,高光效果越强。

5.2镜面反射强度计算( L s L_s Ls

改进:视角向量 v v v和反射向量 R R R接近的时候,其实就说明了法线方向 n n n和半程向量 h h h很接近。即
在这里插入图片描述
( 半 程 向 量 ) h = b i s e c t o r ( v , l ) = v + l ∣ ∣ v + l ∣ ∣ (半程向量)h=bisector(v,l)=\frac{v+l}{||v+l||} ()h=bisector(v,l)=v+lv+l
L s = K s ( I / r 2 ) m a x ( 0 , c o s α ) p = K s ( I / r 2 ) m a x ( 0 , n ⋅ h ) p L_s=K_s(I/r^2)max(0,cos\alpha)^p=K_s(I/r^2)max(0,n·h)^p Ls=Ks(I/r2)max(0,cosα)p=Ks(I/r2)max(0,nh)p

  • h h h:半程向量
  • L s L_s Ls:镜面(高光)反射强度
  • K s K_s Ks:镜面系数,通常认为高光是白色的,因此我们通常认为ks是白的一个颜色。
  • 指 数 p 指数p p:因为 c o s α cos\alpha cosα n ⋅ h n·h nh都是单位向量,具体作用参考下图。

指 数 p 指数p p的作用:
在这里插入图片描述
如上图可知:增加p的值会减小高光范围。
高光通常是非常亮且集中于一个很小的区域内的, 因此我们想要得到的是角度只要稍微改变,其余弦值就会剧烈变化.因此我们引入指数p,p越大,其对应的函数变化也越大,以p=64为例,也就是表示当角度在大约30度时,就不会发生高光了.正常情况下,我们会取指数为100~200。

下图是ks和p对高光效果的影响,同一行使用的材质相同,同一列使用的p相同。
在这里插入图片描述
如上图可知,ks越大,高光越明显;p越大,高光范围越小

6.环境光(Ambient Term)

6.1环境光原理

概念表示: 环境光就是许多光线经过多次漫反射最终打在某一点上,因为环境光太复杂,所以我们假设任何一个点接收到的环境光强度为 L a L_a La,任何一个点肯定有自己的颜色,因此 K a K_a Ka为环境光系数,两项结合在一起我们可以近似的得到环境光。

6.2环境光强度计算( L α L_\alpha Lα

环境光不用考虑光的方向,与观察的方向也无关,也不用考虑法线,因此与v,l,n均无关系,从而可以说明环境光是一个常数。
在这里插入图片描述
L a = K a I a L_a=K_aI_a La=KaIa

  • L a L_a La:环境光强度
  • K a K_a Ka:环境系数

7.合成所有的光( L α L_\alpha Lα + L d L_d Ld+ L s L_s Ls )

在这里插入图片描述
将环境光,漫反射,高光三种光照效果加在一起,就能够得到最终的光照效果,这看起来像是一个塑料。

本文将介绍计算机图形学中的着色(Shading)技术,包括着色频图形管线、纹理映射等知识点,并提供部分代码示例。 ## 着色频 计算机图形学中的着色可以分为两种频,分别是顶点着色和像素着色。 顶点着色(Vertex Shading)是在顶点级别对图形进行着色的过程,即在图形的每个顶点上计算颜色值,然后通过插值计算出整个图形的颜色。顶点着色通常用于处理顶点属性,如位置、法向量和颜色等。 像素着色(Pixel Shading)是在像素级别对图形进行着色的过程,即对图形的每个像素计算颜色值。像素着色通常用于处理纹理映射、阴影效果、反射和折射等。 ## 图形管线 图形管线(Graphics Pipeline)是计算机图形学中的一个重要概念,它是将输入的几何形状转化为最终图像的过程,通常包括以下几个阶段: 1. 顶点输入:将输入的顶点数据传入图形管线。 2. 顶点着色:在顶点级别对图形进行着色。 3. 图元装配:将顶点组装成图元,如点、线、三角形等。 4. 光栅化:将图元转化为像素,并计算像素在屏幕上的位置。 5. 像素着色:对图形的每个像素进行着色。 6. 输出合成:将所有像素合成成最终的图像。 以下是一个简单的图形管线示例: ```c++ // 顶点着色器 void vertexShader(in vec3 position, out vec4 color) { // 计算顶点颜色 color = vec4(1.0, 0.0, 0.0, 1.0); // 将顶点位置传递给下一个阶段 gl_Position = vec4(position, 1.0); } // 像素着色器 void pixelShader(in vec4 color, out vec4 fragmentColor) { // 直接输出顶点颜色 fragmentColor = color; } // 主程序 int main() { // 顶点数据 vec3 vertices[] = { vec3(-1.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(1.0, -1.0, 0.0) }; // 图元数据 GLuint indices[] = {0, 1, 2}; // 创建着色器程序 GLuint program = createProgram(vertexShader, pixelShader); // 获取顶点着色器输入位置的位置 GLuint positionLocation = glGetAttribLocation(program, "position"); // 创建顶点数组对象 GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // 创建顶点缓冲区对象 GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点数据传递给顶点着色器 glEnableVertexAttribArray(positionLocation); glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); // 创建索引缓冲区对象 GLuint ibo; glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 渲染图元 glUseProgram(program); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); // 销毁着色器程序、缓冲区对象和顶点数组对象 glDeleteProgram(program); glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &ibo); glDeleteVertexArrays(1, &vao); return 0; } ``` ## 纹理映射 纹理映射(Texture Mapping)是一种基于图像的着色技术,它可以在三维模型表面上贴上图片,从而增强模型的真实感和细节。 纹理映射通常包括以下几个步骤: 1. 加载纹理图像:从文件中加载纹理图像,并将其存储在计算机内存中。 2. 创建纹理对象:将纹理图像传递给图形硬件,并创建一个纹理对象。 3. 纹理坐标计算:计算每个顶点在纹理图像中对应的位置,通常使用二维纹理坐标表示。 4. 纹理采样:在纹理图像中根据纹理坐标采样像素颜色,并将其作为顶点颜色。 5. 顶点着色:使用顶点颜色进行顶点着色。 以下是一个简单的纹理映射示例: ```c++ // 顶点着色器 void vertexShader(in vec3 position, in vec2 texCoord, out vec2 vTexCoord) { // 将纹理坐标传递给下一个阶段 vTexCoord = texCoord; // 将顶点位置传递给下一个阶段 gl_Position = vec4(position, 1.0); } // 像素着色器 uniform sampler2D texture; void pixelShader(in vec2 vTexCoord, out vec4 fragmentColor) { // 在纹理图像中采样像素颜色 vec4 texel = texture2D(texture, vTexCoord); // 输出纹理像素颜色 fragmentColor = texel; } // 主程序 int main() { // 顶点数据 vec3 vertices[] = { vec3(-1.0, -1.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(1.0, -1.0, 0.0) }; // 纹理坐标数据 vec2 texCoords[] = { vec2(0.0, 0.0), vec2(0.5, 1.0), vec2(1.0, 0.0) }; // 图元数据 GLuint indices[] = {0, 1, 2}; // 加载纹理图像 GLuint texture = loadTexture("texture.png"); // 创建着色器程序 GLuint program = createProgram(vertexShader, pixelShader); // 获取顶点着色器输入位置和纹理坐标的位置 GLuint positionLocation = glGetAttribLocation(program, "position"); GLuint texCoordLocation = glGetAttribLocation(program, "texCoord"); // 创建顶点数组对象 GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // 创建顶点缓冲区对象 GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 将顶点数据传递给顶点着色器 glEnableVertexAttribArray(positionLocation); glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); // 创建纹理坐标缓冲区对象 GLuint tbo; glGenBuffers(1, &tbo); glBindBuffer(GL_ARRAY_BUFFER, tbo); glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW); // 将纹理坐标数据传递给顶点着色器 glEnableVertexAttribArray(texCoordLocation); glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, 0); // 创建索引缓冲区对象 GLuint ibo; glGenBuffers(1, &ibo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 渲染图元 glUseProgram(program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); glUniform1i(glGetUniformLocation(program, "texture"), 0); glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0); // 销毁着色器程序、缓冲区对象、纹理对象和顶点数组对象 glDeleteProgram(program); glDeleteBuffers(1, &vbo); glDeleteBuffers(1, &tbo); glDeleteBuffers(1, &ibo); glDeleteTextures(1, &texture); glDeleteVertexArrays(1, &vao); return 0; } ``` 以上是计算机图形学中着色技术的简单介绍,希望对您有所帮助。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值