在场景中添加光线——添加HLSL Vertex Shading
问题
使用你配置好的光照,BasicEffect可以很好地绘制场景。但是,如果你想定义一些更酷的效果,首先要实现的就是正确的光照。
本教程中,你将学习如何编写一个基本的HLSL effect实现逐顶点光照。
解决方案
传递每个顶点的3D位置和法线到effect中。显卡上的vertex shader需要对每个顶点做两件事。
首先,当绘制3D世界时,总是要使用世界矩阵,视矩阵和投影矩阵将3D位置转换为对应的2D屏幕坐标。
第二,通过叉乘光线方向和法线方向计算顶点的光照强度。
工作原理
首先需要在XNA项目中定义顶点。显然你需要将3D位置存储在每个顶点中。要在vertex shader 中计算正确的光照,你还需要为每个顶点提供法线,可参见教程6-1理解法线的概念。
你可以使用教程6-1中的相同代码,这个代码创建了包含一个3D位置和一个法线(还包含纹理坐标,只是这里你不使用它们)的六个顶点。
在XNA项目中创建一个新的. fx文件,添加以下代码。它包含了可以从XNA应用程序中改变的HLSL变量。
float4x4 xWorld; float4x4 xView; float4x4 xProjection; float xAmbient; float3 xLightDirection;
当将一个3D坐标转换到2D屏幕坐标时,总是需要视矩阵和投影矩阵(见教程2-1)。因为你还想在场景中移动物体,所以还需一个世界矩阵(见教程4-2)。因为这个教材处理的是光照,你需要定义光线的方向。Ambient变量让你可以设置光照的最小级别,这样,即使一个对象没有被光源直接照射,它仍是隐约可见的。
在进入vertex shader和pixel shader前,首先需要定义output结构。首先,vertex shader的output 就是pixel shader的input,必须保存每个顶点的2D屏幕坐标。第二,vertex shader 还计算了每个顶点的光照强度。
在vertex shader和pixel shader之间,这些值进行了插值,让每个像素获取了它们各自的插值。
pixel shader仅计算每个像素的最终颜色。
struct VSVertexToPixel {float4 Position : POSITION; float LightingFactor : TEXCOORD0; };struct VSPixelToFrame {float4 Color : COLOR0; };
Vertex Shader
vertex shader将World,View和Projection矩阵组合成一个矩阵,用来将3D坐标转换为2D屏幕坐标。
给定光线方向和法线方向,vertex shader可以根据图6-7计算光照强度。光线和法线间的夹角越小,光照越强烈,夹角越大,光照越少。
你可以通过点乘这两个方向获得这个值。点乘返回一个0到1之间的值(如果两个向量的长度都是1)。
图6-7 光线方向和法线方向的点乘
但是,在计算两者的点乘时首先要将其中一个方向反向,否则这两个方向是相反的。例如,图6-7右图中你发现法线和光线方向是相反的,这会导致点乘的结果为负。
VSVertexToPixel VSVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0) {VSVertexToPixel Output = (VSVertexToPixel)0; float4x4 preViewProjection = mul(xView, xProjection); float4x4 preWorldViewProjection = mul(xWorld, preViewProjection); Output.Position = mul(inPos, preWorldViewProjection); float3 normal = normalize(inNormal); Output.LightFactor = dot(rotNormal, -xLightDirection); return Output; }
点乘的结果是一个single值,基于两个法线的夹角和长度。在大多数情况中,你只需要光线基于两者的夹角。这意味着你需要确保3D空间中的所有法线和光线方向长度是一样的;否则,具有更长法线的顶点会获取更多的光照。
这可以通过让所有法线的长度为1做到,即需要归一化法线。
注意:归一化(normalizing)的意思不是对法线不做操作,而是让一个向量的长度变为1,可参见教程6-1。
使用世界矩阵时确保正确的光照
前面的代码在世界矩阵为单位矩阵时工作良好,即物体需要放置在(0,0,0)3D空间的初始位置(见教程5-2)。
但在大多数情况中,你想使用另外的世界矩阵,让你可以移动/旋转/缩放对象。
如图6-1所示,如果你旋转了物体,法线也会跟着一起旋转。这意味着法线需要通过世界矩阵中的旋转量进行变换。
世界矩阵中的缩放操作不会影响光照的计算。你总要在vertex shader中归一化法线,让向量的长度变为1。
但是,如果世界矩阵中包含平移,你就会遇到麻烦。这是因为法线是最大长度为1的向量。例如,当你使用一个包含超过两个单位的矩阵变换法线时,所有的法线都会指向那个方向。
如图6-8所示,一个物体使用一个包含平移一段距离的世界矩阵向右平移时,顶点的位置会移向右方。法线也会根据这个世界矩阵移向右方,但它们的方向应该是不变的。所以,在使用世界矩阵变换法线时,你需要将世界矩阵中的平移部分剥离出来。
图6-8 被世界矩阵中的平移影响的法线
矩阵是一个包含4 × 4个数字的表格。你应该只使用世界矩阵中的旋转部分变换法线,而不要用平移部分。你可以提取出矩阵的旋转部分,它位于左上的3 × 3的数字中。只需简单地将4 × 4世界矩阵变换为一个3 × 3矩阵,就可以只获取旋转信息,这正是你所需要的!使用这个矩阵旋转法线,代码如下所示:
float3 normal = normalize(inNormal); float3x3 rotMatrix = (float3x3)xWorld; float3 rotNormal = mul(normal, rotMatrix); Output.LightFactor = dot(rotNormal, -xLightDirection);
Pixel Shader
首先,三角形的三个顶点由vertex shader进行处理,计算光照值。然后,对三角形中的每个像素,这个光照值会在三个顶点间进行插值。这个插值过的光照值传递到pixel shader。
在这个简单地例子中,取蓝色为物体的基本颜色。要在三角形上添加明暗效果,要将这个基本颜色乘以LightFactor (在前面的vertex shader中计算)和环境光照(由XNA程序通过xAmbient变量设置)。环境光(ambient)因子确保所有物体不会是完全黑暗的,而LightFactor 根据光线方向施加对应的光照:
VSPixelToFrame VSPixelShader(VSVertexToPixel PSIn) : COLOR0 {VSPixelToFrame Output = (VSPixelToFrame)0; float4 baseColor = float4(0,0,1,1); Output.Color = baseColor*(PSIn.LightFactor+xAmbient); return Output; }
定义technique
最后,定义technique:
technique VertexShading {pass Pass0 {VertexShader = compile vs_2_0 VSVertexShader(); PixelShader = compile ps_2_0 VSPixelShader(); } }
XNA代码
在XNA项目中,导入HLSL文件并将它存储在一个Effect变量中,这和教程3-1中对纹理的操作是类似的。在本例中,HLSL文件名为vertexshading. fx:
effect = content.Load<Effect>("vertexshading");
当绘制物体时,首先需要设置effect的参数,这需要用到BasicEffect:
effect.CurrentTechnique = effect.Techniques["VertexShading"]; effect.Parameters["xWorld"].SetValue(Matrix.Identity); effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); effect.Parameters["xLightDirection"].SetValue(new Vector3(1, 0, 0)); effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); device.VertexDeclaration = myVertexDeclaration; device.DrawUserPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, vertices, 0, 2); pass.End(); } effect.End();
代码
XNA代码绘制对象的多个实例。因为使用了不同的世界矩阵,这些对象会绘制在不同位置。
最终结果和教程6-1是一样的,只是这次你使用了自己的HLSL effect:
effect.CurrentTechnique = effect.Techniques["VertexShading"]; effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix); effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix); effect.Parameters["xLightDirection"].SetValue(new Vector3(1, 0, 0)); effect.Parameters["xAmbient"].SetValue(0.0f); for (int i = 0; i < 9; i++) {Matrix world = Matrix.CreateTranslation(4, 0, 0) * Matrix.CreateRotationZ((float)i * MathHelper.PiOver2 / 8.0f); effect.Parameters["xWorld"].SetValue(world); effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) {pass.Begin(); device.VertexDeclaration = myVertexDeclaration; device.DrawUserPrimitives<VertexPositionNormalTexture> (PrimitiveType.TriangleList, vertices, 0, 2); pass.End(); }effect.End(); }
下面是.fx文件的完整内容:
float4x4 xWorld; float4x4 xView; float4x4 xProjection; float xAmbient; float3 xLightDirection; struct VSVertexToPixel {float4 Position : POSITION; float LightFactor : TEXCOORD0; }; struct VSPixelToFrame {float4 Color : COLOR0; }// Technique: VertexShading VSVertexToPixel VSVertexShader(float4 inPos: POSITION0, float3 inNormal: NORMAL0) {VSVertexToPixel Output = (VSVertexToPixel)0; float4x4 preViewProjection = mul(xView, xProjection); float4x4 preWorldViewProjection = mul(xWorld, preViewProjection); Output.Position = mul(inPos, preWorldViewProjection); float3 normal = normalize(inNormal); float3x3 rotMatrix = (float3x3)xWorld; float3 rotNormal = mul(normal, rotMatrix); Output.LightFactor = dot(rotNormal, -xLightDirection); return Output; }VSPixelToFrame VSPixelShader(VSVertexToPixel PSIn) : COLOR0 { VSPixelToFrame Output = (VSPixelToFrame)0; float4 baseColor = float4(0,0,1,1); Output.Color = baseColor*(PSIn.LightFactor+xAmbient); return Output; }technique VertexShading {pass Pass0 {VertexShader = compile vs_2_0 VSVertexShader(); PixelShader = compile ps_2_0 VSPixelShader(); } }
转载于:https://www.cnblogs.com/AlexCheng/archive/2011/02/14/2120098.html
在场景中添加光线——添加HLSL Vertex Shading相关推荐
- [unreal4入门系列之十] UE4添加角色到场景中
现在我们已经有了一个场景并且运行了,我们需要添加一个角色到场景中.要这样做,我们必须从UE4的GameFramework类继承它. 一. 打开上次创建的关卡 如果你已经关闭了项目,并且保存了上次创建的 ...
- 在场景中添加光线——在Deferred Shading引擎中添加阴影能力
问题 虽然你已经掌握了基本的计算机实时光照,但你应该注意到光源还没有投射出阴影.这是因为pixel shader是基于光线与法线的夹角计算光照的.直到现在,pixel shader还没有考虑到光线与像 ...
- js添加网页水印和three.js场景中加水印
我们在日常网页开发的时候,可能想给自己的网页或者canvas里面添加水印,增添个人标记,我这里分为普通静态html页面和threejs中3d场景里面添加水印功能. 一 静态html页面添加水印 你只需 ...
- [unreal4入门系列之八] 使用VS编译UE4编辑器并添加物体到场景中
接下来,我们将会创建一个放置我们的游戏角色的基本场景. 编译UE4编辑器 1) 现在,我们创建一个空白的UE4项目来开始.首先点击桌面的Epic Games Launcher,打开启动器,点击左上角的 ...
- NVisionXR_iOS教程八 —— 场景中添加粒子
本章节将介绍如何往场景里添加粒子效果.打开上一章节的代码 1.添加头文件#import ''NVWorldParticles.h'' 2.将粒子的资源文件拖入到UserRes.bundle目录下( ...
- 【Unity3D】使用 FBX 格式的外部模型 ( 向 Unity 中添加 FBX 模型 | 向 Scene 场景中添加 FBX 模型 | 3D 物体渲染 | 3D 物体材质设置 )
文章目录 一.向 Unity 中添加 FBX 模型 二.向 Scene 场景中添加 FBX 模型 三.3D 物体渲染 四.3D 物体材质设置 一.向 Unity 中添加 FBX 模型 Unity 中使 ...
- UE4 学习记录八 给场景中添加背景音乐和动画音效,运动加速效果
这只是用来记录我学习UE4过程的,可能帮不到你,先说声抱歉.为了防止误导他人,请勿转载,请勿转载,请勿转载. 本文的主题是给场景中添加背景音乐和动画的音效.总章目录(https://blog.csdn ...
- 【企业数字化转型】数据可视化技术:Three.js 用Physijs在场景中添加物理效果
Three.js 极简教程 简介 Three.JS 是什么 Three.JS是基于WebGL的Javascript开源框架,简言之,就是能够实现3D效果的JS库. Three.JS 能做什么 利用Th ...
- three.js后期处理-使用UnrealBloomPass通道在场景中添加泛光效果,三维物体表面发光效果(vue中使用three.js85)
使用UnrealBloomPass通道在场景中添加泛光效果 1.demo效果 2. 重要知识点 2.1 回顾要点 2.2 UnrealBloomPass通道介绍 3. 实现要点 3.1 相关文件引入 ...
最新文章
- 数据蒋堂 | “后半”有序的分组
- Unity TIP4: 带泛型参数的接口注入(interface,generic)
- boost::gregorian模块实现使用公历精确地推进一个月的测试程序
- arouter跨module传递消息_利用ARouter实现组件间通信,解决子模块调用主模块问题...
- CSS之七个高度有效的媒体查询技巧
- 计算机专业英语词汇pdf,计算机专业英语词汇1700词.pdf
- Excel怎么删除表格最后的空行
- python实现规则引擎_规则引擎python
- 德国IT行业薪酬水平大揭秘--2020
- ssm+boot+thymeleaf博客系统完成总结
- 卫星遥感技术应用与农业干旱监测的研究进展
- HTTP Status 500 - Servlet.init() for servlet DispatcherServlet threw exception
- 关于掉落的详细分析。
- 独立开发者:新手做2D手游该用哪些工具
- 卡布奇诺搭建教程_移动Web应用程序框架匹配,第2部分,探索卡布奇诺咖啡以进行移动Web应用程序开发
- sqljdbc4.jar和sqljdbc.jar下载
- “数字炸弹“——练习Python基础知识的小游戏【文末源码地址】
- OSChina 周日乱弹 —— 请务必让我分担他们的痛苦!
- 银河麒麟V10 - postgresql/postgis完整部署
- 特斯拉冻结招聘或因「幽灵刹车」故障,全球大厂掀裁员潮人心惶惶
热门文章
- php+js实现异步图片上传,JavaScript实现异步图像上传功能
- 数据库连接php_php 连接 数据库
- java 三级菜单栏的添加_[Java教程]jquery实现的三级导航菜单实例代码
- shell mysql awk_shell mysql 处理数据小结
- oracle没用过元数据,案例:Oracle RAC asm备份元数据之md_backup和md_restore 好处与
- 【补丁分析】CVE-2016-8610:对导致拒绝服务的“SSL Death Alert”漏洞补丁分析
- 信安教程第二版-第11章网络物理隔离技术原理与应用
- ipv4广播地址怎么填_什么是IP地址?IP地址有什么用?网络工程师来告诉你
- div 重新加载_JS之 加载模糊文本动画
- MongoDB模糊查询-查询某月的数据