提要

在上一篇文章中,我们介绍了简单的Shading,同时提出了一个光照模型,模拟了一个点光源,但是,关于光的故事还没有结束...

今天要学习的是方向光源(Directional Light),聚光灯,per pixel shading,halfway vector。

关于光源的原理及数学描述,请参考:光线追踪(RayTracing)算法理论与实践(三)光照

方向光源

方向光源就两个参数,方向和强度。

还是简单的 ambient + diffuse + spec 光照模型。先看shader的代码。

basic.vert

#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;  out vec3 LightIntensity;struct LightInfo{vec4 Direction;vec3 Intensity;
};struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;
};uniform LightInfo Light;
uniform MaterialInfo Material;uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;void getEyeSpace(out vec3 norm, out vec4 position)
{norm =  normalize(NormalMatrix * VertexNormal);position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}vec3 ads(vec4 position, vec3 norm)
{vec3 s;if(Light.Direction.w == 0.0)s = normalize(vec3(Light.Direction));elses = normalize(vec3(Light.Direction - position));vec3 v = normalize(vec3(-position));vec3 r = reflect(-s, norm);return Light.Intensity * (Material.Ka + Material.Kd*max(dot(s,norm), 0.0) + Material.Ks * pow(max(dot(r,v),0.0), Material.Shininess));
}void main()
{vec3 eyeNorm;vec4 eyePosition;getEyeSpace(eyeNorm, eyePosition);LightIntensity = ads(eyePosition, eyeNorm);gl_Position = MVP * vec4( VertexPosition, 1.0);
}

在ads函数中,首先通过nomal矩阵将顶点法向量变换到视口坐标下,(nomal矩阵其实就是model-view矩阵的左上3x3的矩阵)然后通过model-view矩阵将顶点坐标转化为视口坐标系(eye coordinates)下。

接下来的ads用来计算光照模型下顶点的颜色,分别计算三个分量,然后相加。

basic.frag

#version 400in vec3 LightIntensity;void main(void)
{gl_FragColor = vec4(LightIntensity, 1.0);//gl_FragColor = vec4(1.0,1.0,0.1, 1.0);
}

这个就是将根据顶点shader传来的颜色对片段进行赋值。

在cgl.cpp的setUniform函数中对Uniform变量进行赋值。
void CGL::setUniform()
{mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);mat4 mv = view * model;prog.setUniform("Material.Kd", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ka", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ks", 0.8f, 0.8f, 0.8f);prog.setUniform("Material.Shininess", 100.0f);prog.setUniform("Light.Direction", vec4(1.0f, 0.0f, 0.0f, 0.0f));prog.setUniform("Light.Intensity", 1.0f, 1.0f, 1.0f);prog.setUniform("ModelViewMatrix", mv);prog.setUniform("NormalMatrix",mat3( vec3(mv[0]), vec3(mv[1]), vec3(mv[2]) ));prog.setUniform("MVP", projection * mv);}

渲染的效果如下:


可以很明显的感觉模型旋转时到表面光照的变化。

halfway vector 性能优化

在前面的光照模型中,用于计算specular分量的公式如下:

其中 是反射光线的方向向量,v是往视口方向的向量,其中 r 的计算:

这个计算过程会非常耗时,我们可以用一个trick来改善一下。

定义一个 h (halfway vector)向量:

下图是 和其他向量的位置关系。

specular分量的计算就可以转化成:

相比于计算 r ,的计算相对比较简单,而 h 和 n 之间的夹角与 和 r 之间的夹角大小几乎相同!那就意味着我们可以用 h.n 来代替 r.v 从而可以带利用 halfway vector 来获得性能上的一些提升。虽然效果上会有那么小小的不同。

后面的灯光的计算都会用到这个优化。

聚光灯 Spotlight

这里的采用一个最简单的聚光灯模型:

灯光的属性有:位置,强度,方向,衰减(exponent),裁剪角度。

实现起来也比较简单,在投射角内的物体,渲染方式和点光源的计算一样,投射角之外的顶点,着色的时候只有ambient。

还是采用我们比较熟悉的per vertex shading 方式。在vert中定义聚光灯:

//baisc.vert
#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;  out vec3 LightIntensity;struct SpotLightInfo{vec4 position;vec3 direction;vec3 intensity;float exponent;float cutoff;
};struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;
};uniform SpotLightInfo Spot;
uniform MaterialInfo Material;uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;void getEyeSpace(out vec3 norm, out vec4 position)
{norm =  normalize(NormalMatrix * VertexNormal);position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}vec3 adsSpotLight(vec4 position, vec3 norm)
{vec3 s = normalize(vec3(Spot.position - position));float angle = acos(dot(-s, normalize(Spot.direction)));float cutoff = radians(clamp(Spot.cutoff, 0.0, 90.0));vec3 ambient = Spot.intensity * Material.Ka;if(angle < cutoff){float spotFactor = pow(dot(-s, normalize(Spot.direction)), Spot.exponent);vec3 v = normalize(vec3(-position));vec3 h = normalize(v + s);return ambient + spotFactor * Spot.intensity * (Material.Kd * max(dot(s, norm),0.0)+ Material.Ks * pow(max(dot(h,norm), 0.0),Material.Shininess)); }else{return ambient; }}void main()
{vec3 eyeNorm;vec4 eyePosition;getEyeSpace(eyeNorm, eyePosition);LightIntensity = adsSpotLight(eyePosition, eyeNorm);gl_Position = MVP * vec4( VertexPosition, 1.0);
}

几个GLSL中的内置函数在这里说明一下。

genType clamp( genType x, genType minVal, genType maxVal);

获取三个数中第二大的数。

genType radians(genType degrees);

将角度转换成弧度。

adsSpotLight是主要的函数,先计算顶点和光源方向之间的夹角,判断顶点是否在照射的区域,然后分别求得最终的颜色。

片段shader还是那样:

#version 400in vec3 LightIntensity;void main(void)
{gl_FragColor = vec4(LightIntensity, 1.0);
}

uniform变量的赋值:

void CGL::setUniform()
{//model = glm::rotate(this->model, 10.0f, vec3(0.0f,1.0f,0.0f));mat4 projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);mat4 mv = view * model;mat3 normalMatrix = mat3( vec3(view[0]), vec3(view[1]), vec3(view[2]) );prog.setUniform("Material.Kd", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ka", 0.9f, 0.5f, 0.3f);prog.setUniform("Material.Ks", 0.8f, 0.8f, 0.8f);prog.setUniform("Material.Shininess", 100.0f);vec4 lightPos = vec4(1.0f, 5.0f, 20.0f, 1.0f);// prog.setUniform("Spot.position", lightPos);prog.setUniform("Spot.position", view * lightPos);prog.setUniform("Spot.direction", normalMatrix * vec3(-10.0,0.0,-40.0) );//prog.setUniform("Spot.direction", vec3(10.9f,10.9f,10.9f)  );prog.setUniform("Spot.intensity", vec3(0.9f,0.9f,0.9f) );prog.setUniform("Spot.exponent", 30.0f );prog.setUniform("Spot.cutoff", 15.0f );prog.setUniform("ModelViewMatrix", mv);prog.setUniform("NormalMatrix",normalMatrix);prog.setUniform("MVP", projection * mv);}

在这里给Spot.position赋值的时候不是 prog.setUniform("Spot.position", lightPos); 而是prog.setUniform("Spot.position", view * lightPos),因为在shader中的计算都是在视口坐标下进行的,这样做是为了统一坐标。Spot.direction的赋值也是一样。也可以把坐标转换这一步放到shader中去做。

最终效果如下:

逐像素着色 per pixel shading

相对与前面将主要计算工作放在顶点shader中的 per vertex shading ,per pixel shading 指的是将计算放到片段shader中,这样可以带来更加真实可感的渲染效果。

basic2.vert

#version 400
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;  out vec4 Position;
out vec3 Normal;uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;void getEyeSpace(out vec3 norm, out vec4 position)
{norm =  normalize(NormalMatrix * VertexNormal);position = ModelViewMatrix * vec4(VertexPosition, 1.0);
}void main()
{getEyeSpace(Normal, Position);gl_Position = MVP * vec4( VertexPosition, 1.0);
}

basic2.frag

#version 400in vec4 Position;
in vec3 Normal;struct SpotLightInfo{vec4 position;vec3 direction;vec3 intensity;float exponent;float cutoff;
};struct MaterialInfo{vec3 Ka;vec3 Kd;vec3 Ks;float Shininess;
};uniform SpotLightInfo Spot;
uniform MaterialInfo Material;vec3 adsSpotLight(vec4 position, vec3 norm)
{vec3 s = normalize(vec3(Spot.position - position));float angle = acos(dot(-s, normalize(Spot.direction)));float cutoff = radians(clamp(Spot.cutoff, 0.0, 90.0));vec3 ambient = Spot.intensity * Material.Ka;if(angle < cutoff){float spotFactor = pow(dot(-s, normalize(Spot.direction)), Spot.exponent);vec3 v = normalize(vec3(-position));vec3 h = normalize(v + s);return ambient + spotFactor * Spot.intensity * (Material.Kd * max(dot(s, norm),0.0)+ Material.Ks * pow(max(dot(h,norm), 0.0),Material.Shininess)); }else{return ambient; }}void main(void)
{gl_FragColor = vec4(adsSpotLight(Position, Normal), 1.0);//gl_FragColor = vec4(1.0,1.0,0.5, 1.0);
}

看一下渲染效果:

最终的效果还是有一些差别,特别是光线交界的地方。

代码下载

OpenGLPro13

参考

OpenGL 4.0 Shading Language Cookbook

转载于:https://www.cnblogs.com/bbsno1/p/3279753.html

OpenGL进阶(十三) - GLSL光照(Lighting)相关推荐

  1. OpenGL ES on iOS --- 光照进阶

    OpenGL ES on iOS --- 光照进阶 简述 本文记录我记录我学习 坐标体系和矩阵转换的过程,加深学习便于后续查询,可能有些描述不够准确,或者内容不够充实,还请多多指正,共同学习. 光源分 ...

  2. 高质量实时渲染课程笔记(二)——图形学基础回顾(渲染管线、OpenGL入门、GLSL、渲染方程)

    文章目录 1 图形渲染管线 2 OpenGL 2.1 使用OpenGL过程的比喻: 油画过程 2.2 Place objects/models 放这些模型 模型这么摆放 2.3 Set up an e ...

  3. CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果...

    CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果 开始 一图抵千言.首先来看鼠标拖动太阳(光源)的情形. 然后是鼠标拖拽旋转模型的情形. 然后我们移动摄像 ...

  4. C++ Opengl纹理过滤和光照实例源码

    C++ Opengl纹理过滤和光照实例源码 项目开发环境 项目功能 项目演示 项目源码传送门 项目开发环境 开发语言:C++和IDE:VS2017,操作系统Windows版本windows SDK8. ...

  5. OpenGL ES之GLSL实现仿抖音“缩放”“灵魂出窍”“抖动”“闪白”“毛刺”“幻觉”等动态滤镜效果

    无滤镜效果 "动态滤镜"效果的实现准备工作的代码与"无分屏滤镜"中的实现逻辑和流程一致,只需要修改相应的底部item数组及对应的着色器名称等,这里不再说明这部分 ...

  6. OpenGL ES之GLSL实现仿抖音“灰度滤镜”和“颠倒滤镜”效果

    无滤镜 "无滤镜"效果的实现准备工作的代码与"无分屏滤镜"中的实现逻辑和流程一致,只需要修改相应的底部item数组及对应的着色器名称等,这里不再说明这部分内容, ...

  7. OpenGL基础45:光照矫正(下)之Gamma校正

    接上文:OpenGL基础44:光照矫正(上) 四.Gamma矫正 4.1.人的视觉特性 和很多错视图一样,对于下面这张灰阶图,如果1表示纯白,0表示纯黑,那么这张图片的哪个位置代表的是0.5,也就是自 ...

  8. OpenGL ES之GLSL实现多种“马赛克滤镜”效果

    ⻢赛克效果 "⻢赛克效果"就是把图⽚的⼀个相当⼤⼩的区域⽤同⼀个点的颜⾊来表示,可以认为是⼤规模的降低图像的分辨率,⽽让图像的⼀些细节隐藏起来. 无马赛克滤镜 "无滤镜& ...

  9. 【OpenGL】笔记十三、光照贴图

    1. 流程 简单的光照材质无法满足要求,接下来我们要对不同材质的物体在各个片段的反射做出精准控制 1.1 漫反射贴图 之前,我们对物体的环境光和漫反射做了简单的单色定义,现在我们加入纹理贴图,让纹理的 ...

最新文章

  1. php合并数组中相同的元素
  2. 用Vue框架和后台请求的时候传递的参数的方式
  3. java快递柜系统开发_他专注智能柜系统开发,产品日使用频次达60万次,服务近400厂家...
  4. 解決 centos -bash: vim: command not found
  5. 【转】 ID,ClientID和UniqueID
  6. 怎能错过这个技术集市!转发有奖,惊喜连连!
  7. linux和android学习,android学习笔记
  8. SGU 325 Palindrome(贪心)
  9. 如何删除 macOS 压缩包中的隐藏文件?
  10. 就是要你懂Java中volatile关键字实现原理
  11. IDEA设置背景与字体大小
  12. 大数据入门的知识体系,大数据学习路线
  13. 一台变两台,电脑也分身
  14. win7安装中文语言包
  15. 双非本科地信前端面试题目
  16. 管理好你的20~30岁
  17. mininet 主机双网关拓扑设计
  18. 基于标定板的手眼标定
  19. matplotlib之pyplot教程
  20. 国内短信服务商厂商比较

热门文章

  1. oracle异地迁移,数据泵实现Oracle数据迁移到异地库
  2. dma工作时cpu工不工作_CPU如何工作?
  3. 测试驱动开发 测试前移_测试驱动开发简介
  4. 热闹的聚会与尴尬的聚会_如何增加(和保存)您最喜欢的技术聚会
  5. (C++)CSP202009-1 称检测点查询
  6. 软件体系结构风格之C/S,三层C/S,与BS
  7. 找Java培训机构需要注意那些
  8. ethereumjs/ethereumjs-vm-2-API文档
  9. 试过不用循环语句撸代码吗?
  10. 在SQL中使用CRL函数示例