http://blog.csdn.net/racehorse/article/details/6662540

逐像素的方向光(Directional Light per Pixel)
这一节将把前面的shader代码改为逐像素计算的方向光。我们需要将工作按照两个shader拆分,以确定哪些是需要逐像素操作的。
首先看看每个顶点接收到的信息:
•法线
•半向量
•光源方向
我们需要将法线变换到视点空间然后归一化。我们还需要将半向量和光源方向也归一化,不过它们已经位于视点空间中了。这些归一化之后的向量会进行插值,然后送入片断shader,所以需要声明易变变量保存这些向量。
我们也可以在顶点shader中完成一些与光和材质相关的计算,这样可以帮助平衡顶点shader和片断shader的负载。
顶点shader代码可以写成如下形式:

  1. varying vec4 diffuse,ambient;
  2. varying vec3 normal,lightDir,halfVector;
  3. void main()
  4. {
  5. /* first transform the normal into eye space and
  6. normalize the result */
  7. normal = normalize(gl_NormalMatrix * gl_Normal);
  8. /* now normalize the light's direction. Note that
  9. according to the OpenGL specification, the light
  10. is stored in eye space. Also since we're talking about
  11. a directional light, the position field is actually direction */
  12. lightDir = normalize(vec3(gl_LightSource[0].position));
  13. /* Normalize the halfVector to pass it to the fragment shader */
  14. halfVector = normalize(gl_LightSource[0].halfVector.xyz);
  15. /* Compute the diffuse, ambient and globalAmbient terms */
  16. diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
  17. ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
  18. ambient += gl_FrontMaterial.ambient * gl_LightModel.ambient;
  19. gl_Position = ftransform();
  20. }

接下来在片断shader中,首先要声明同样的易变变量。此外还要再次对法线进行归一化,光线向量不需要进行归一化了,因为方向光对所有顶点都是一致的,插值得到的结果自然也不会变。之后就是计算插值过的法线向量与光线向量的点积。

  1. varying vec4 diffuse,ambient;
  2. varying vec3 normal,lightDir,halfVector;
  3. void main()
  4. {
  5. vec3 n,halfV;
  6. float NdotL,NdotHV;
  7. /* The ambient term will always be present */
  8. vec4 color = ambient;
  9. /* a fragment shader can't write a varying variable, hence we need
  10. a new variable to store the normalized interpolated normal */
  11. n = normalize(normal);
  12. /* compute the dot product between normal and ldir */
  13. NdotL = max(dot(n,lightDir),0.0);
  14. ...

如果点积结果NdotL大于0,我们就必须计算散射光,也就是用顶点shader传过来的散射项乘以这个点积。我们还需要计算镜面反射光,计算时首先对接收到的半向量归一化,然后计算半向量和法线之间的点积。

  1. ...
  2. if (NdotL > 0.0)
  3. {
  4. color += diffuse * NdotL;
  5. halfV = normalize(halfVector);
  6. NdotHV = max(dot(n,halfV),0.0);
  7. color += gl_FrontMaterial.specular *
  8. gl_LightSource[0].specular *
  9. pow(NdotHV, gl_FrontMaterial.shininess);
  10. }
  11. gl_FragColor = color;

下图显示了逐像素光照和逐顶点光照效果的区别:

本节内容Shader Designer的工程下载地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/dirpixsd.zip

逐像素的点光(Point Light Per Pixel)
本节基于前面有关方向光的内容,大部分代码都相同。本节内容主要涉及方向光和点光的不同之处。方向光一般假设光源在无限远的地方,所以到达物体时是平行光。相反,点光源有一个空间中的位置,并向四面八方辐射光线。此外,点光的强度会随到达顶点的距离而衰弱。
对于OpenGL程序来说,这两种光的区别主要有:
•光源的position域的w分量:对方向光来说它是0,表面这个position实际是一个方向(direction);对点光来说,这个分量是1。
•点光源的衰减由三个系数决定:一个常数项,一个线性项和一个二次项。
对方向光来说,光线的方向对所有顶点相同,但是对点光来说,方向是从顶点指向光源位置的向量。因此对我们来说需要修改的就是在顶点shader中加入计算光线方向的内容。
在OpenGL中衰减是按照如下公式计算的:

式中k0是常数衰减系数,k1是线性衰减系数,k2是二次衰减系数,d是光源位置到顶点的距离。
注意衰减与距离是非线性关系,所以我们不能逐顶点计算衰减再在片断shader中使用插值结果,不过我们可以在顶点shader中计算距离,然后在片断shader中使用距离的插值计算衰减。
使用点光计算颜色值的公式为:

在上面公式中,环境光部分必须分解为两项:使用光照模型的全局环境光设置和光源中的环境光设置。顶点shader也必须分别计算这两个环境光成分。新的顶点shader如下:

  1. varying vec4 diffuse,ambientGlobal,ambient;
  2. varying vec3 normal,lightDir,halfVector;
  3. varying float dist;
  4. void main()
  5. {
  6. vec4 ecPos;
  7. vec3 aux;
  8. normal = normalize(gl_NormalMatrix * gl_Normal);
  9. /* these are the new lines of code to compute the light's direction */
  10. ecPos = gl_ModelViewMatrix * gl_Vertex;
  11. aux = vec3(gl_LightSource[0].position-ecPos);
  12. lightDir = normalize(aux);
  13. dist = length(aux);
  14. halfVector = normalize(gl_LightSource[0].halfVector.xyz);
  15. /* Compute the diffuse, ambient and globalAmbient terms */
  16. diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
  17. /* The ambient terms have been separated since one of them */
  18. /* suffers attenuation */
  19. ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
  20. ambientGlobal = gl_FrontMaterial.ambient * gl_LightModel.ambient;
  21. gl_Position = ftransform();
  22. }

在片断shader中需要计算衰减,还需要将插值得到的光线方向向量归一化,因为一般来说照到每个顶点的光线方向都不同。

  1. varying vec4 diffuse,ambientGlobal, ambient;
  2. varying vec3 normal,lightDir,halfVector;
  3. varying float dist;
  4. void main()
  5. {
  6. vec3 n,halfV,viewV,ldir;
  7. float NdotL,NdotHV;
  8. vec4 color = ambientGlobal;
  9. float att;
  10. /* a fragment shader can't write a varying variable, hence we need
  11. a new variable to store the normalized interpolated normal */
  12. n = normalize(normal);
  13. /* compute the dot product between normal and normalized lightdir */
  14. NdotL = max(dot(n,normalize(lightDir)),0.0);
  15. if (NdotL > 0.0)
  16. {
  17. att = 1.0 / (gl_LightSource[0].constantAttenuation +
  18. gl_LightSource[0].linearAttenuation * dist +
  19. gl_LightSource[0].quadraticAttenuation * dist * dist);
  20. color += att * (diffuse * NdotL + ambient);
  21. halfV = normalize(halfVector);
  22. NdotHV = max(dot(n,halfV),0.0);
  23. color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular *
  24. pow(NdotHV,gl_FrontMaterial.shininess);
  25. }
  26. gl_FragColor = color;
  27. }

下图显示了固定功能的逐顶点与本节中逐像素计算得到的点光效果:

本节内容Shader Designer工程下载地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/pointlightsd.zip

逐像素的聚光(Spot Light Per Pixel)
本节内容与上一节基本一致,唯一不同的就是聚光不同于点光,其发出的光线被限制在一个圆锥体中。
对于OpenGL程序来说,这两种光的区别主要有:
•聚光包含一个方向向量spotDirection,表示圆锥体的轴。
•圆锥体包含一个角度,在GLSL中可以使用应用程序设置的角度值以及对应的余弦值spotCosCutoff。
•最后还有一个衰减速率spotExponent,它表示从圆锥的中心轴向外表面变化时光强度的衰减。
聚光的顶点shader与点光完全相同,我们只需要对片断shader进行一些修改。只有当当前片断位于聚光的光锥内时,才需要对散射光、镜面反射光和环境光成分进行着色。所以我们首先要检查这个条件。
光源与某点连线向量以及聚光方向向量(spotDirection)之间夹角的余弦值必须大于spotCosCutoff,否则此点位于聚光之外,只能接收到全局环境光。

  1. ...
  2. n = normalize(normal);
  3. /* compute the dot product between normal and ldir */
  4. NdotL = max(dot(n,normalize(lightDir)),0.0);
  5. if (NdotL > 0.0)
  6. {
  7. spotEffect = dot(normalize(gl_LightSource[0].spotDirection),
  8. normalize(-lightDir));
  9. if (spotEffect > gl_LightSource[0].spotCosCutoff)
  10. {
  11. /* compute the illumination in here */
  12. }
  13. }
  14. gl_FragColor = ...

下面的光照计算与点光非常相似,唯一区别是衰减必须乘以聚光效果(spotlight effect),这个值按如下公式计算:

上式中spotDirection来自
OpenGL中设置的状态,lightDir是光源到某点的向量,spotExp是聚光衰减率,这个值也是在OpenGL程序中设置的,它用来控制从聚光
光锥中心到边缘的衰减。spotExp越大衰减越快,如果为0表示在光锥内光强是常数。

  1. spotEffect = pow(spotEffect, gl_LightSource[0].spotExponent);
  2. att = spotEffect / (gl_LightSource[0].constantAttenuation +
  3. gl_LightSource[0].linearAttenuation * dist +
  4. gl_LightSource[0].quadraticAttenuation * dist * dist);
  5. color += att * (diffuse * NdotL + ambient);
  6. halfV = normalize(halfVector);
  7. NdotHV = max(dot(n,halfV),0.0);
  8. color += att * gl_FrontMaterial.specular *
  9. gl_LightSource[0].specular *
  10. pow(NdotHV,gl_FrontMaterial.shininess);

下图分别显示了使用固定功能流水线的逐顶点光照计算,以及使用本节shader的逐像素光照计算得到的聚光效果。

本节内容Shader Designer的工程下载地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/spotlightsd.zip

【GLSL教程】(七)逐像素的光照 【转】的更多相关文章

  1. 【GLSL教程】(六)逐顶点的光照 【转】

    引言 在OpenGL中有三种类型的光:方向光(directional).点光(point).聚光(spotlight).本教程将从方向光讲起,首先我们将使用GLSL来模仿OpenGL中的光. 我们将向 ...

  2. GLSL逐像素光照 【转】

    转载:http://blog.csdn.net/hgl868/article/details/7872414 逐像素的方向光(Directional Light per Pixel) 这一节将把前面的 ...

  3. unity shader入门(三)逐像素光照,Blinn-Phong模型

    与上篇逐顶点光照很像,只是改为在片元着色器中计算光照,下为逐像素光照shader Shader "study/Chapter6/PixelShader"{ Properties{ ...

  4. 【GLSL教程】(一)图形流水线 【转】

    http://blog.csdn.net/racehorse/article/details/6593719 这是一些列来自lighthouse3d的GLSL教程,非常适合入门.我将边学习边翻译该教程 ...

  5. Unity shader学习之逐像素漫反射光照模型

    shader如下: Shader "Custom/Diffuse Fragment-Level" { Properties { _Diffuse (,,,) } SubShader ...

  6. CRL快速开发框架系列教程七(使用事务)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  7. Laravel教程 七:表单验证 Validation

    Laravel教程 七:表单验证 Validation 此文章为原创文章,未经同意,禁止转载. Laravel Form 终于要更新这个Laravel系列教程的第七篇了,期间去写了一点其他的东西. 就 ...

  8. 无废话ExtJs 入门教程七[登陆窗体Demo:Login]

    无废话ExtJs 入门教程七[登陆窗体Demo:Login] extjs技术交流,欢迎加群(201926085) 在这节我们通过前几节讲的内容做一个登陆页面,把前几节讲的内容贯穿一下. 1.代码如下: ...

  9. ASP.NET 5系列教程(七)完结篇-解读代码

    在本文中,我们将一起查看TodoController 类代码. [Route] 属性定义了Controller的URL 模板: [Route("api/[controller]") ...

随机推荐

  1. [转载]用等高线图(Contour maps)可视化多变量函数

    https://blog.csdn.net/xlinsist/article/details/50920479 Overview 由于我们用手来画三维图像很困难,我们可以用等高线图来描述图像会更加简单 ...

  2. tomcat 服务不支持 chkconfig 以及其他服务不能添加到开机启动时的操作

    在安装完tomcat后想添加的开机自启动的操作,但是报错tomcat 服务不支持 chkconfig,后来在  /etc/init.d/tomcat中的#!/bin/bash后添加上#chkconfi ...

  3. DOM的相关概念

    [前面的话]DOM全称是Document Object Model,即文档对象模型.我们常说的html文档其实就是一个DOM树,DOM操作就是在内存中找到DOM树上我们想要的DOM对象,对它的属性进行 ...

  4. 【Luogu】P4067储能表(数位DP)

    题目链接 好的 看到这题之后我一直在想反演,然后想不出来,一度以为自己脑子有问题 然后我脑子真的有问题,这题tm根本就不是反演 设f[i][j][k][l]表示现在已经DP到从高位往低数的第i位,有没 ...

  5. xctf --Hctf2014 Quals write up

    描述 猫流大大发现一个女神,你能告诉我女神的名字么(名字即是flag) nvshen.zip Solution: Extract the file and we could find a txt wh ...

  6. input上传多张图片

    input的file上传多张图片的时候,用ajaxupload这个插件的时候,每次执行完,需要重新生成元素再绑定事件

  7. CSDN数据库下载地址 CSDN 用户名密码泄漏,600万数据下载

    原文发布时间为:2011-12-21 -- 来源于本人的百度文章 [由搬家工具导入] 12月21日消息,下午有网友爆料称国内最大的开发者社区CSDN.NET的安全系统遭到黑客攻击,CSDN数据库中的6 ...

  8. PE笔记之NT头PE扩展头

    typedef struct _IMAGE_OPTIONAL_HEADER {       //       // Standard fields.       //       WORD    Ma ...

  9. Day 30 process&thread_2

    进程和线程_2 1.继承类创建线程 import threading,time class Mythread(threading.Thread): #建立类,继承threading.Thread de ...

  10. web项目中引入jquery easyui

    jQuery easyui是一个基于jquery的用户界面插件集合,可以做出各种炫酷页面效果,大中型项目都可以使用些框架,非常好用,而且它有中文网,提供了大量的demo.下面我们看怎么将它引入到项目中 ...