如何实现 shadowgun unity map unity 聚光灯 cookie

1878人阅读
3DEngine(11)
Shadow Mapping是一种基于图像空间的阴影实现方法,其优点是实现简单,适应于大型动态场景;缺点是由于shadow map的分辨率有限,使得阴影边缘容易出现锯齿(Aliasing);关于SM的研究很活跃,主要围绕着阴影抗锯齿,出现了很多SM变种,如PSM,LPSM,VSM等等,在这里http://en.wikipedia.org/wiki/Shadow_mapping可以找到很多SM变种的链接;;SM的实现分为两个pass,第一个pass以投射阴影的灯光为视点渲染得到一幅深度图纹理,该纹理就叫Shadow
Map;第二个pass从摄像机渲染场景,但必须在ps中计算像素在灯光坐标系中的深度值,并与Shadow Map中的相应深度值进行比较以确定该像素是否处于阴影区;经过这两个pass最终就可以为场景打上阴影。这篇文章主要总结一下自己在实现基本SM的过程中遇到的一些问题以及解决方法,下面进入正题。
1、生成Shadow Map
为了从灯光角度渲染生成Shadow Map,有两个问题需要解决:一是要渲染哪些物体,二是摄像机的参数怎么设置。对于问题一,显然我们没必要渲染场景中的所有物体,但是只渲染当前摄像机视景体中的物体又不够,因为视景体之外的有些物体也可能投射阴影到视景体之内的物体,所以渲染Shadow Map时,这些物体必须考虑进来,否则可能会出现阴影随着摄像机的移动时有时无的现象,综上,我们只需要渲染位于当前摄像机视景体内的所有物体以及视景体之外但是会投射阴影到视景体之内的物体上的物体,把它们的集合称为阴影投射集,为了确定阴影投射集,可以根据灯光位置以及当前的视景体计算出一个凸壳,位于该凸壳中的物体才需要渲染,如图1所示。对于问题二,灯光处摄像机的视景体应该包含阴影投射集中的所有物体,另外应该让物体尽量占据设置的视口,以提高Shadow
Map的精度;对于方向光和聚光灯,摄像机的look向量可以设置为光的发射发向,摄像机的位置设置为灯光所在的位置,为了包含阴影集中的所有物体,可以计算阴影投射集在灯光视图空间中的轴向包围盒,然后根据面向光源的那个面设置正交投影参数,就可以保证投射集中的所有物体都位于灯光视景体中,并且刚好占据整个视口,如图2所示。更详细的信息可以参考《Mathematics for 3D Game Programming and Computer Graphics, Third Edition》一书的10.2节。
图1:灯光位置与视景体构成一个凸壳,与该凸壳相交的物体称为阴影投射集
图2:根据阴影投射集在灯光视图空间中的包围盒来计算正交投影参数
2、生成阴影场景
生成了ShadowMap之后,就可以根据相应灯光的视图矩阵和投影矩阵从摄像机角度渲染带有阴影的场景了。下面把相关的shader代码贴上:
vertex shader:
// for projective texturing
uniform mat4 worldM
uniform mat4 lightViewM
uniform mat4 lightProjM
varying vec4 projTexC
void main()
// for projective texture mapping
projTexCoord = lightProjMatrix*lightViewMatrix*worldMatrix*
// map project tex coord to [0,1]
projTexCoord.x = (projTexCoord.x + projTexCoord.w)*0.5;
projTexCoord.y = (projTexCoord.y + projTexCoord.w)*0.5;
projTexCoord.z = (projTexCoord.z + projTexCoord.w)*0.5;
gl_Position = gl_ModelViewProjectionMatrix *
pixel shader(版本一):
// The shadow map
uniform sampler2DShadow shadowDepthM
varying vec4 projTexC
void main()
// light computing
vec4 lightColor = Lighting(...);
vec4 texcoord = projTexC
texcoord.x /= texcoord.w;
texcoord.y /= texcoord.w;
texcoord.z /= texcoord.w;
// depth comparison
vec4 color = vec4(1.0,1.0,1.0,1.0);
float depth = texture(shadowDepthMap,vec3(texcoord.xy,0.0));
if(texcoord.z & depth)
// this pixel is in shadow area
color = vec4(0.6,0.6,0.6,1.0);
gl_FragColor = lightColor*
这样就实现了最基本的Shadow Mapping,对于每个像素只采样一个深度texel,看看效果吧。
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 图3:最基本的Shadow Mapping
可以看到效果不尽如人意,主要有三个问题(分别对应上图标记):1、物体的背光面当作阴影处理了;2、正对着光的一些像素也划到阴影区去了(Self-shadowing);3、阴影边缘有比较强的锯齿效果。对于问题一,可以判断当前像素是否背着灯光,方法是求得像素在灯光视图空间中的位置以及法线,然后求一个点积就可以了,对于这种像素,不用进行阴影计算即可;问题二产生的原因是深度误差导致的,当物体表面在灯光视图空间中的倾斜度越大时,误差也越大;解决办法有多种,第一种是在进行深度比较时,将深度值减去一个阈值再进行比较,第二种是在生成shadow
map时,只绘制背面,即将背面设置反转,第三种方法是使用OpenGL提供的depth offset,在生成shadow map时,给深度值都加上一个跟像素斜率相关的值;第一种方法阈值比较难确定,无法完全解决Self-shadowing问题,第二种方法在场景中都是二维流形物体时可以工作的很好,第三种方法在绝大多数情况都可以工作得很好,这里使用这种方法,如下面代码所示:
// handle depth precision problem
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0f,1.0f);
// 绘制阴影投射集中的物体
glDisable(GL_POLYGON_OFFSET_FILL);
解决第一和第二个问题后的效果如下面所示图4所示:
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 图4:解决背光面阴影和Self-shadowing问题之后的效果
还有最后一个问题未解决,从上面的图也可看得出来,阴影边缘锯齿比较严重,解决这个问题也有两种较常用方法,第一种方法是用PCF(Percentage Closer Filtering),基本思想是对每个像素从shadow map中采样相邻的多个值,然后对每个值都进行深度比较,如果该像素处于阴影区就把比较结果记为0,否则记为1,最后把比较结果全部加起来除以采样点的个数就可以得到一个百分比p,表示其处在阴影区的可能性,若p为0代表该像素完全处于阴影区,若p为1表示完全不处于阴影区,最后根据p值设定混合系数即可。第二种方法是在阴影渲染pass中不计算光照,只计算阴影,可以得到一幅黑白二值图像,黑色的表示阴影,白色表示非阴影,然后对这幅图像进行高斯模糊,以对阴影边缘进行平滑,以减小据齿效果,最后在光照pass中将像素的光照值与相应二值图像中的值相乘就可以得到打上柔和阴影的场景了;在理论上来说,两种方法都能达到柔和阴影的效果,本文采用的PCF方法,第二种方法后面会尝试,并在效果和速度上与PCF做一下比较,等测试完了会贴到这里来。下面贴上加了PCF的像素shader的代码:
// The shadow map
uniform sampler2DShadow shadowDepthM
varying vec4 projTexC
void main()
// light computing
vec4 lightColor = Lighting(...);
float shadeFactor = 0.0;
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2(-1, -1));
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2(-1, 1));
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2( 1, -1));
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2( 1, 1));
shadeFactor *= 0.25;
// map from [0.0,1.0] to [0.6,1.0]
shadeFactor = shadeFactor * 0.4 + 0.6;
gl_FragColor = lightColor*shadeF
另外注意要对shadow map纹理设置以下纹理参数:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE,GL_COMPARE_REF_TO_TEXTURE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL);
PCF效果如下图 所示,可以看到阴影边缘变柔和了:
&&&&&&&&&&&&&&&&&&&&&&&&&&& 图5:柔和的阴影边缘(PCF:采样pattern:左上,左下,右下,右上)
不同的采样pattern对结果也会有影响,比如采用下面的采样pattern效果如图6所示:
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2(-1.5, 0.5));
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2(0.5, 0.5));
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2(-1.5, -1.5));
shadeFactor += textureProjOffset(shadowDepthMap, projTexCoord, ivec2(0.5, -1.5));
&&&&&&&&&&&&&&&&&&&&&&&&&&&& 图6:柔和的阴影边缘(PCF:使用非对齐采样pattern)
最后再贴一个2048分变率的shadow map产生的阴影效果,以作比较,PCF的采样pattern跟图6中一样。
&图7:shadow map大小为2048时的阴影效果(PCF采样pattern与图6一样)。
关于Shadow Map的变种有很多,不同的变种针对不同情况不同场景提供SM阴影抗锯齿解决方案,本文实现的只是基本的SM,后面考虑实现某种SM变种,进一步提高阴影的效果。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:62563次
积分:1064
积分:1064
排名:千里之外
原创:38篇
转载:18篇
评论:11条
(1)(2)(4)(1)(3)(8)(38)结合Projector和Rendertexture实现实时阴影及软阴影效果 - 简书
下载简书移动应用
写了2675字,被6人关注,获得了4个喜欢
结合Projector和Rendertexture实现实时阴影及软阴影效果
说到实时阴影的实现,一般比较容易想到使用ShadowMap,通过投射灯光空间的深度图,并在投射物体上进行深度比较,判断是否处于阴影的范围,以此来渲染阴影。深度图投射到接受阴影的物体上的效果如图所示:
深度图投射
你所需要做的就是在灯光空间渲染一张深度纹理,并投射到接受阴影的物体上,并和接受阴影的物体上对应像素位置的深度(灯光空间)进行比较,来确定当前像素是否处于阴影即可,此外还要考虑深度图的精度以及以此会造成的ZFighting等,当然这并不是本文讨论的重点。本文主要介绍一种直接投射灯光空间摄像机的Rendertexture来实现阴影的方法,并将在稍后将其和projector结合。当然同时熟悉这两项技术的开发者应该已经清楚,使用projector实现阴影意味着你将会消耗额外的drawcall,实际上被投射projector并且未在shader中使用"IgnoreProjector"="true"的物体都会在自身shader渲染完(也可能是渲染前,具体看自身渲染队列和projector的shader的渲染队列的先后顺序)后再次使用projector的shader渲染一次。首先比较一下这种技术和shadowmap技术,实际上个人感觉很大程度上两者的技术其实差不多,都需要用到屏幕投影,只不过shadowmap投射的是深度图(深度缓冲),而本文介绍的是直接投射屏幕纹理(帧缓冲),因此投射的纹理是带Alpha通道的,
注意灯光空间的摄像机背景颜色的Alpha因为0,否则会把背景色也投射到接受阴影的物体上
和shadowmap不同的是,灯光空间的摄像机应该只看到投射阴影的物体:
此时投射后的效果大致如图所示:
当然使用这种方式投射rendertexture必然造成的一个问题是,由于没有投射接受阴影的平面,导致一旦投射阴影的物体穿透接受阴影的物体时会造成阴影的穿帮:
阴影投射物体穿透阴影接受物体
接受阴影物体Shader主要实现代码:
其中viewMatrix为灯光空间摄像机的worldToCameraMatrix,projMatrix为灯光空间摄像机的投影矩阵。当然使用这种方式实现阴影的不足之处在于需要明确的知道投射阴影的物体和接受阴影的物体。接下来将尝试将其与Projector结合,注意之前已经讨论过,使用projector意味着额外的drawcall,尤其是场景中物件很多且全部都是分离的物体时,不建议使用这种方式。当然如果场景中只有极少部分物体需要接受阴影,比如只有主要地形,则不妨可以尝试使用这种方式,因为使用projector,你可以很方便的在shader中加入IgnoreProjector标签来忽略投影机的作用,或者直接在projector上修改projector影响的层。从unity标准资源包中的projector shader我们大致可以了解,projector shader中需要两个4阶矩阵,分别为_Projector和_ProjectorClip,其中后者主要用于近远裁面的淡入淡出,并不是必须的。而前者的_Projector,注意这个矩阵应该区别于摄像机的projection矩阵(尽管摄像机和projector在很多参数上很相似),原因是官方的projector shader中直接通过:o.uvShadow = mul (_Projector, vertex);计算得到投影纹理坐标,这意味着_Projector矩阵应该同时实现将vertex转换到世界空间,再转换到projector的局部空间,最后转换到projector的投影空间的功能,所以其性质应该类似UNITY_MATRIX_MVP矩阵,所以使用projector实现投射rendertexture的效果,只需要添加一个脚本,其会创建一个摄像机,并使用projector的参数,并将这个摄像机的rendertexture传递给projector的material,具体实现如下:
其中_FadeTex是一张表示阴影衰减的贴图,其r、g通道效果如下:
这是实现后的阴影效果:
这里由于使用的RT是原始摄像机尺寸的1/2所以阴影质量有所下降
另外由于投射的是带Alpha通道的Rendertexture,意味着可以方便的对其使用模糊shader完成模糊效果,这里是我自己编写的模糊脚本效果图:
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
· 52人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:您所在的位置: &
聚光灯光源(Spotlight)
聚光灯光源(Spotlight)
吴亚峰/于复兴
人民邮电出版社
《Unity 3D游戏开发技术详解与典型案例》第6章3D游戏开发的常用技术,本章介绍了3D开发过程中常用的开发技术,其中包括天空盒的应用,虚拟按钮与摇杆的使用,声音的添加与控制,光源的添加与控制,地形引擎的应用,角色动画的使用以及角色控制器的应用。本节为大家介绍聚光灯光源(Spotlight)。
6.4.3& 聚光灯光源(Spotlight)
聚光灯光源即灯光从一点发出,只在一个方向按照一个锥形物体的范围照射,范围由聚光灯的角度和光照范围所决定,其就像是个手电筒,其光照只影响锥形区域内的所有物体。
在场景中添加聚光灯光源很简单,即单击GameObject→Create Other→Spotlight菜单,即可在当前场景中创建一个聚光灯光源,在游戏组成对象列表中选中刚刚创建的Spotlight,在属性查看器中可以看到聚光灯光源的具体属性以及默认的设置,如图6-40所示。
提示&在实际的开发中一般的默认设置是不足以满足开发的需求的,在不同的需求下需要进行不同设置。
下面就对聚光灯光源的具体属性进行介绍,具体的属性信息如表6-9所示。
▲图6-40& 聚光灯光源的属性信息
表6-9 &聚光灯光源的具体属性信息
【责任编辑: TEL:(010)】&&&&&&
关于&&&&的更多文章
使用 iOS 6游戏开发入门经典(第2版) ,学习为iPhone 5和新iPad构
本书描述了黑客用默默无闻的行动为数字世界照亮了一条道路的故事。
多年来,Imar Spaanjaars一直是ASP.NET相关图书的畅销
《C#高级编程(第8版)》是C# 2012和.NET 4.5高级技术的
《Android 4 游戏入门经典(第3版)》将赋予您惊人的灵
本书为《Eclipse从入门到精通》一书的全新改版。本书以最新的Eclipse 3.2作为写作版本。全书分为5篇:起步篇介绍了Eclipse及相关
51CTO旗下网站How do I sample a shadowmap in a custom shader? - Unity Answers
Navigation
Unity account
You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio.
How do I sample a shadowmap in a custom shader?
I just signed up with Unity 3 Pro and am writing custom shaders for my game.
Unity shadows work for stock shaders: you just have to set all shadow casters and receivers in your scene and shadows magically work. But how about custom shaders? I would like to tap the shadow map myself to apply shadows to receivers, does anyone know how to do this? I tried sampling _ShadowMapTexture but this texture is always white.
Best Answer
For the sake of anyone else who is trying to write a fragment shader that receives shadows, I figured it out.
You must do these things:
'#include &AutoLight.cginc&'
'#include &Lighting.cginc&'
Add &Tags {&LightMode& = &ForwardBase&}
'#pragma multi_compile_fwdbase'
Add the Unity macros to your VSOut struct, VS and PS: LIGHTING_COORDS, TRANSFER_VERTEX_TO_FRAGMENT, LIGHT_ATTENUATION.
None of this is documented in the Unity manual.
To access the primary directional light color, unity_LightColor[0] works as long as you don't add &Tags {&LightMode& = &ForwardBase&}&. If you do add that line, then it doesn't work: use _LightColor0.rgb instead. Why? Who knows.. probably makes sense to someone with access to the Unity source code. Which means no one.
Good luck!
Shader &Custom/PeterShader2& {
Properties
_MainTex (&Base (RGB)&, 2D) = &white& {}
#include &UnityCG.cginc&
#include &AutoLight.cginc&
#include &Lighting.cginc&
uniform sampler2D _MainT
Tags { &RenderType&=&Opaque& }
Lighting On
Tags {&LightMode& = &ForwardBase&}
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
struct VSOut
float4 pos
: SV_POSITION;
: TEXCOORD1;
LIGHTING_COORDS(3,4)
VSOut vert(appdata_tan v)
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord.
TRANSFER_VERTEX_TO_FRAGMENT(o);
float4 frag(VSOut i) : COLOR
float3 lightColor = _LightColor0.
float3 lightDir = _WorldSpaceLightPos0;
float4 colorTex = tex2D(_MainTex, i.uv.xy * float2(25.0f));
atten = LIGHT_ATTENUATION(i);
float3 N = float3(0.0f, 1.0f, 0.0f);
NL = saturate(dot(N, lightDir));
float3 color = colorTex.rgb * lightColor * NL *
return float4(color, colorTex.a);
FallBack &Diffuse&
MAKE NOTE that you can't do this if Tags {&Queue&=&Transparent&} !
You can, if you add
#pragma multi_compile_fwdadd_fullshadows
- equally undocumented :)
How do i detect if shadows are activated or compatible with current platform ? I would like to use another sub-shader if it's not the case. And can't find a right way to do that.
Hint: You can notify a user about this post by typing @username
Attachments: Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.
9 People are following this question.

我要回帖

更多关于 unity blob shadow 的文章

 

随机推荐