当前位置: 首页 > 编程日记 > 正文

OpenGL进阶(十三) - GLSL光照(Lighting)

提要

在上一篇文章中,我们介绍了简单的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

相关文章:

Web漏洞扫描(二:Windows server2008 R2操作系统(部署dvwa))

在Windows server 2008 R2系统中部署dvwa; 1、在Windows server 2008虚拟机中配置IIS&#xff1b; 1.1、打开服务器管理器&#xff0c;角色&#xff0c;添加角色&#xff0c;然后点击下一步&#xff1b; 1.2、选择安装“Web 服务器&#xff08;IIS&#xff09;”&#xff0c;…

[iOS]调和 pop 手势导致 AVPlayer 播放卡顿

作者 NewPan 关注 2017.07.15 14:24* 字数 3110 阅读 749评论 8喜欢 17声明&#xff1a;我为这个框架写了四篇文章&#xff1a; 第一篇&#xff1a;[iOS]UINavigationController全屏pop之为每个控制器自定义UINavigationBar 第二篇&#xff1a;[iOS]UINavigationController全屏…

Cocos2d-x学习笔记(三十)之 游戏存档

游戏中的存档功能可以保证玩家在游戏过程中有足够的延续性&#xff0c;这点在单机游戏开发中尤为重要。Cocos2D-x中支持的游戏存档类CCUserDefault可以作为一个轻量化的数据库来使用。它支持存储的数据类型包括bool&#xff08;布尔型&#xff09;、int&#xff08;整型&#x…

github删除文件夹

git rm -rf dirgit add .git commit -m remove dirgit push origin master //dir是要删除的文件夹路径转载于:https://www.cnblogs.com/xulei1992/p/5650399.html

Web漏洞扫描(三:Burp Suite的基本操作)

任务二、Burp Suite基础Proxy功能&#xff1b; 2.1、在Kali虚拟机中打开Burp Suite工具并设置&#xff0c;打开“Proxy”选项卡&#xff0c;选中“Options”子选项卡&#xff0c;单机“Add”按钮&#xff0c;增加一个监听代理&#xff0c;设置为127.0.0.1:8080&#xff1b; 2.…

UITableView嵌套WKWebView的那些坑

最近项目中遇到了一个需求&#xff0c;TableView中需要嵌套Web页面&#xff0c;我的解决办法是在系统的UITableViewCell中添加WKWebView。开发的过程中&#xff0c;遇到了些坑&#xff0c;写出来分享一下。 1.首先说一下WKWebView的代理方法中&#xff0c;页面加载完成后会走的…

深入了解line-height

1.定义 行高:两行文字baseline(基线)之间的距离 示意图&#xff1a; 2.为何line-height可以让单行文本垂直居中 其实并没有垂直居中&#xff0c;除非将font-size:0; 3.line-height的高度原理&#xff08;可以先看看行内盒子的原理&#xff09; * 行内元素的高度是lin…

实现一个简单的投票功能

实现一个简单的投票功能 最近项目中需要用到一个投票功能&#xff0c;当时觉得简单&#xff0c;向都没想就动手开始做&#xff0c;没想到走了不少弯路。 后来才发现&#xff0c;是想的太过简单了。来看看改进后的功能。 第一步&#xff1a;数据库设计 两个表&#xff1a;一个主…

Web漏洞扫描(四:知识点及错误总结)

WVS软件&#xff1a; WVS(Web Vulnerability Scanner)是一个自动化的Web应用程序安全测试工具&#xff0c;它可以扫描任何可通过Web浏览器访问的和遵循HTTP/HTTPS规则的Web站点和Web应用程序。适用于任何中小型和大型企业的内联网、外延网和面向客户、雇员、厂商和其它人员的W…

【VS开发】【智能语音处理】Windows下麦克风语音采集

简介 这是我很早以前的大学毕业设计,忽然间找到贴出来以纪念自己的纯真年代...但是因为CSDN不给面子所以导致短短的一篇文章贴了足足7次..他老提时说文章超过了64K,老大,拜托,那是算上了里面的图片大小吧...:-( 本文简单介绍了声卡的工作原理 , 录音的原理以及数字音频的基本知…

iOS音频——AudioToolbox

一、前言 二、音频文件Audio File Services 三、音频文件转换Extended Audio File Services 四、音频流Audio File Stream Services 五、音频队列Audio Queue Services 一、前言 AudioToolbox提供的API主要是C 使用起来相对晦涩&#xff0c;针对本文提供了简单的代码示例减小学…

【WA】九度OJ题目1435:迷瘴

题目描述&#xff1a; 通过悬崖的yifenfei&#xff0c;又面临着幽谷的考验——幽谷周围瘴气弥漫&#xff0c;静的可怕&#xff0c;隐约可见地上堆满了骷髅。由于此处长年不见天日&#xff0c;导致空气中布满了毒素&#xff0c;一旦吸入体内&#xff0c;便会全身溃烂而死。幸好y…

云端应用SQL注入攻击

实验目的及要求&#xff1a; 完成VMware Workstations14平台安装&#xff0c;会应用相关操作&#xff1b;完成Windows server2008R2操作系统及Kali Linux操作系统的安装&#xff1b;掌握SQLmap攻击工具的使用&#xff1b;使用SQLmap对目标站点进行渗透攻击&#xff1b; 实验环…

SPOJ375(树链剖分)

题目&#xff1a;Query on a tree 题意&#xff1a;给定一棵树&#xff0c;告诉了每条边的权值&#xff0c;然后给出两种操作&#xff1a; (1)把第i条边的权值改为val (2)询问a&#xff0c;b路径上权值最大的边 分析&#xff1a;本题与HDU3966差不多&#xff0c;区别就是&#…

简单的python服务器程序

一个接受telnet输入的服务器端小程序 #!/usr/local/bin/python3.5 #coding:utf-8 import sockethost port 51423s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((host, port)) s.listen(1)print(S…

iOS:一句代码实现文本输入的限制

前言 实际开发中&#xff0c;往往需要处理UITextView、UITextField输入的限制。比如输入必须是价格格式&#xff08;一个小数点、小数点后面最多两位&#xff09;&#xff1b;输入最大长度限制&#xff1b;对输入内容的实时回调。处理这些的时候&#xff0c;我们通常需要做一些…

Linux操作系统(一:基本操作)

1、创建一个以自己姓名拼音简写一致的用户名&#xff08; useradd -d /home/abc abc&#xff09; 2、在linux中使用打印命令&#xff0c;在命令行中输入“当前用户名Hello world !” 3、显示现在天年月日&#xff0c;显示后一天的日期&#xff0c;显示上一月日期&#xff0c;显…

Core Text 学习笔记-基础

前言 最近在学习YYKit框架&#xff0c;看到关于CoreText相关的知识的时候感到非常吃力&#xff0c;于是乎就恶补了一下Core Text相关的基础知识。 Glyphs&#xff08;字形&#xff09; 字符的图形形式&#xff0c; 则是文字中字母 (character) 的视觉表现。&#xff08;字形&am…

虚拟机下CentOS 6.5配置IP地址的三种方法

实验软件环境&#xff1a;虚拟机Vmware Workstation10.0 、CentOS 6.5 32位 1、自动获取IP地址 虚拟机使用桥接模式&#xff0c;相当于连接到物理机的网络里&#xff0c;物理机网络有DHCP服务器自动分配IP地址。 dhclient 自动获取ip地址命令 ifconfig 查询系统里网卡信息&…

GIS 相关知识扫盲

1、什么是GIS GIS:地理信息系统&#xff0c;它是一种特定的十分重要的空间信息系统。它是在计算机硬、软件系统支持下&#xff0c;对整个或部分地球表层&#xff08;包括大气层&#xff09;空间中的有关地理分布数据进行采集、储存、管理、运算、分析、显示和描述的技术系统。2…

Linux操作系统(二:shell脚本)

练习一&#xff1a;编写shell脚本&#xff0c;计算1-100的和&#xff1b; 练习二&#xff1a;将一目录下所有的文件的扩展名改为bak 练习三&#xff1a;写一个脚本&#xff0c;统计。/etc/ 目录下共有多少个目录文件 练习四&#xff1a;写一个脚本&#xff0c;依次向/etc/passw…

用OpenGLES实现yuv420p视频播放界面

背景 例子TFLive这个项目里&#xff0c;是我按着ijkPlayer写的直播播放器&#xff0c;要运行需要编译ffmpeg的库,网盘里存了一份, 提取码&#xff1a;vjce。OpenGL ES播放相关的在在OpenGLES的文件夹里。 learnOpenGL学到会使用纹理就可以了。 播放视频&#xff0c;就是把画面一…

C++类的静态成员详细讲解

在C中&#xff0c;静态成员是属于整个类的而不是某个对象&#xff0c;静态成员变量只存储一份供所有对象共用。所以在所有对象中都可以共享它。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则&#xff0c;保证了安全性还可以节省内存。 静态成员的定义或声明要…

jenkins2 multibranch

通过multibranch类型的pipeline job使得对于多个branch的支持更加简单。只需要创建一个multibranch job&#xff0c;jenkins将自动地为所有的branch创建job。 文章来自&#xff1a;http://www.ciandcd.com文中的代码来自可以从github下载&#xff1a; https://github.com/ciand…

Nagios的安装和基本配置(一:知识点总结及环境准备)

实验目的及要求 掌握Nagios监控的基本使用&#xff1b;掌握Nagios监控服务的搭建和配置&#xff1b; 实验环境&#xff1a; 1、满足实验要求的PC端&#xff1b; Host-name OS IP sofaware Nagios-server Centos7 192.168.1.119 Apache,php,Nagios,Nagios-plguins Nag…

浅谈Android四大组件之Service

一&#xff1a;Service简介 Android开发中&#xff0c;当需要创建在后台运行的程序的时候&#xff0c;就要使用到Service。 1:Service&#xff08;服务&#xff09;是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service&#xff0c;并且当用户切…

使用 fastlane 实现 iOS 持续集成(二)

本文接上篇文章主要说下怎样使用 fastlane 上传到fir和蒲公英&#xff0c;下面先介绍下 plugin 命令。 plugin命令介绍: 列出所有可用插件 fastlane search_plugins 搜索指定名称的插件: fastlane search_plugins [query] 添加插件: fastlane add_plugin [name] 安装插件: fast…

Nagios的安装和基本配置(二:Nagios-Server的安装)

任务二、Nagios-server的安装 2.1、创建Nagios用户和组 注&#xff1a; #useradd Nagios -s /bin/nologin #groundadd nagcmd #usermod -a -G nagcmd Nagios #usermod -a G nagcmd apache 2.2、安装Nagios 2.2.1、上传软件包至操作系统中&#xff1b; 2.2.2、解压软件并…

shell编程-正则表达式

1.正则表达式是什么 它主要用于字符串的模式分割&#xff0c;匹配&#xff0c;查找及替换操作。 2、正则表达式与通配符 正则表达式用来在文件中匹配符合条件的字符串&#xff0c;正则包含匹配。grep,awk,sed等命令可以支持正则表达式。 通配符用来匹配符合条件的文件名&#x…

使用 CocoaPods 给微信集成 SDK 打印收发消息

推荐序 本文介绍的是一套逆向工具&#xff0c;可以在非越狱手机上给任意应用增加插件。在文末的示例中&#xff0c;作者拿微信举例&#xff0c;展示出在微信中打印收发消息的功能。 这套工具可以加快逆向开发的速度&#xff0c;其重签名思想也可以用于二次分发别人的应用。 其实…