Unity的unity3d 5 实时阴影影效率低吗

游戏开发(3)
unity3D(2)
图形编程(2)
UnityEffects系列(1)
UnityEffects(2)之(2)ProjectorShadow(手游上的实时阴影方案)
上一篇说到了在unity5.x中实现shadowMap的方法()。但是由于它需要使用DepthTexture有一定的硬件要求(参考这里:),而且是一个posteffect(后期效果),在手机上面还是效率不高。
手游上面的人物阴影一般都是比较取巧的方法(毕竟没有这么多人用机皇:),基本上遵循“够用就好,看起来不错就好”的原则),一种是给人物脚底贴一个黑圈圈,第二种是使用projector。第一种就不提了,效果不好。这里我们就讨论一下后者,使用投影来实现手游中的实时阴影。据说猪场的《功夫熊猫3》中的阴影也是使用类似的方法实现的,效果不错,但是阴影投影到台阶这种不接受阴影的物体就会有一点小穿帮,也是这种方式的一个缺点,如下图:
建筑物等静态物体产生的阴影一般用光照烘焙来实现,这里就不讨论了。
江湖惯例先贴图:
工程放在:&使用unity5.4
场景是Scene/(2)ProjectorShadow
实现的原理和shadowMap差不多,只不过不生成深度图,而是使用replaceShader生成一张“轮廓”图,使用untiy自带的Projector组件投影到场景中,在shader中计算从而生成阴影效果。使用replaceShader是为了避免渲染“轮廓”图的时候还是用原来复杂的shader,照成不必要的性能浪费。替换后我们只输出纯色(方便测试)或者alpha值到“轮廓”图。
我们生成的“轮廓”图:
投影的时候只需要判断该像素的alpha值,alpha为0的不在阴影中,alpha值越小阴影越透明。
由于原理和上一篇差不多就不做过多解释了,这里列两点比较值得注意的:
1、渲染“轮廓”图的相机视锥(光锥)的生成方式我们使用了通过场景的包围盒来校正的方法,这种方式比较适合手游,毕竟多数手游需要动态阴影的就是那些人物(其他的都是静态阴影通过光照烘培来实现),这样最大化的利用的这张“轮廓”图,提高了阴影的质量。对应方法是ShadowUtils. SetLightCamera(Bounds b, Camera lightCamera)
参考资料:
2、当“轮廓”图中的物体贴近边缘的时候阴影会产生bug,如下图,看到阴影被拉长了一条。
这是由于“轮廓”图的wrapMode采用使用了clamp导致的,clamp模式就是uv在超过1的时候把图像边缘的像素拉伸,如下图:
如何解决呢?两个方法。
1)求包围盒的时候设置一个offset,使包围盒向外扩大一点,这样物体就不会贴着“轮廓”图的边缘了
2)在投影器的shader中使用一张图来做遮罩(下图),这样边缘的像素可以过滤掉,还可以做出边缘的阴影渐隐的效果。
上面两种方法都可以从代码中找到。
下面我们来对比一下shadowMap,分析一下各自的优缺点:
通用,物体穿透不会穿帮,能很好的处理遮挡关系。
1.依赖DepthTexture,浮点深度图,有一点硬件需求。
有硬件要求:
2.unity中深度图不能单独输出,要手动写shader从自带的depthMap中取出深度来保存,比较麻烦。使用替换渲染时,不产生阴影的物体必须替换为白色,不替换会默认黑色,然后黑色又是深度最小值,会使得物体都处于阴影之中,不好控制。
3.产生和接受阴影的物体都要渲染到深度图
4.比较深度的误差偏移有可能根据平台而不太一样
5.要为接受阴影的物体提供shader
ProjectorShadow
1.不依赖DepthTexture
2.不用为接受阴影的物体提供shader
3.只需要渲染产生阴影的物体
4.性能比shadowMap有优势
1.是一个trick,不通用
2.不能处理物体对阴影的遮挡,接受阴影的物体和产生阴影的物体有穿透时会穿帮
总体来说ProjectorShadow比较适合移动平台,能模拟出效果不错的实时阴影
扩展:还可以使用pcf优化阴影边界
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1871次
排名:千里之外Unity自带的阴影系统是否会比定制实现的一套阴影系统效率低,低在哪里?在unity里如何较好的实现自己的一套阴影系统?现在上线的unity游戏都是用的自带阴影吗?
手机游戏的话 unity3D的实时阴影是用不了的,性能扛不住。一般只给主角加,是自己用个单独摄像机拍个Rt铺地表上,有个插件叫fastshadow
你指的定制的阴影系统是什么?影子这种东西,都是在核心部分就会实现出来的,同样的一个算法,即使有差异,在这些公司手里应该都差不多(都是C++写)。但是如果你说的定制的是在脚本层来实现,我想肯定会差一点。
你指的定制的阴影系统是什么?影子这种东西,都是在核心部分就会实现出来的,同样的一个算法,即使有差异,在这些公司手里应该都差不多(都是C++写)。但是如果你说的定制的是在脚本层来实现,我想肯定会差一点。
unity默认的那套是shadow map,有cast/receive所以需要额外的pass(不过具体哪个变体不清楚)&br&你可以考虑lightmap烘焙,或者用projector&br&&br&你想自己实现一套也不是不可以,就是很麻烦,因为它的渲染管线你不好嵌入
unity默认的那套是shadow map,有cast/receive所以需要额外的pass(不过具体哪个变体不清楚) 你可以考虑lightmap烘焙,或者用projector 你想自己实现一套也不是不可以,就是很麻烦,因为它的渲染管线你不好嵌入
已有帐号?
无法登录?
社交帐号登录后使用快捷导航没有帐号?
只需一步,快速开始
查看: 2907|回复: 22
Unity实时高效阴影(实在没卷了,来赚一点点就好)
TA的其他好贴
马上注册,加入CGJOY,享用更多功能,让你轻松玩转CGJOY。
才可以下载或查看,没有帐号?
无意间看到一篇文章,说是Unity5 demo中为了实现角色的良好阴影,单独给角色设计了一个角色阴影系统。而且使用的是比较老的技术,但效果很好。其实在很多时候,我们需要的并不是万能的阴影光照系统,而是局部能做到效果就行。
& && &&&万能的好处在于任何情况都能看上去合理,但是相对的,性能开销也大,同时为了兼顾各种情况,只能做各种效果的折中,所以我们看到了现在移动平台上,要么就是没有实时阴影,要么就是充满锯齿的实时阴影,要么就是使用2D贴图来模拟实时阴影。
& && &&&用2D贴图来模拟的效果毫无疑问是最好的,但问题在于成本太高,很多小团队资金有限,很难专门为每一个角色都让美术画一大堆阴影贴图。而这也毫无疑问会增加游戏的大小。
& && &&&我主要思考的是,在某种条件下,是否可以实现局部的良好的阴影。比如角色展台,毫无疑问只会出现一个角色,那么这个情况下,毫无疑问我们需要的是一个完美的阴影。或者说某一些游戏,视角固定,而且能看到的范围很小,那么是否只针对这个部分去实现好的阴影系统。或者一个很小的室内,我们也需要一个好的角色阴影。
& && &&&ok,那么开始思考方案,首先我们应该只需要一个平行光的阴影。一般来说需要获得这个位置看过去的深度图。我首先在这个位置上放了一个正交摄像机,注意如果你想让角色有阴影,那么必须让角色处在这个正交摄像机的范围内,那么现在第一个问题来了,如何保证角色在正交摄像机的范围内?
& && &&&方法如下:首先你要获得你主摄像机内的所有的需要阴影的物体,然后将这些物体转化到正交摄像机的坐标中,计算出这些物体的最大范围,并得出正交矩阵赋值给正交摄像机。(代码借鉴了,对这个楼主深表感谢)
& && &&&这里要注意,Unity计算出来的Z是负值,但OpenGL是正的,官方说明如下:
Matrix that transforms from world to camera space.
Use this to calculate the camera space position of objects or to provide customcamera's location that is not based on the transform.
Note that camera space matches OpenGL convention: camera's forward is the negativeZ axis. This is different from Unity's convention, where forward is the positive Zaxis.
If you change this matrix, the camera no longer updates its rendering based on its Transform.This lasts until you call ResetWorldToCameraMatrix.
#pragma strict
// Offsets camera's rendering from the transform's position.
public var offset: Vector3 = new Vector3(0, 1, 0);
var camera: C
function Start() {
& & camera = GetComponent.&Camera&();
function LateUpdate() {
& & var camoffset: Vector3 = new Vector3(-offset.x, -offset.y, offset.z);
& & var m: Matrix4x4 = Matrix4x4.TRS(camoffset, Quaternion.identity, new Vector3(1, 1, -1));
& & camera.worldToCameraMatrix = m * transform.worldToLocalM
不过实际使用过程中,我们也许并不需要正确的矩阵赋值,因为你需要的是保证所有的物体在摄像机范围内,只需要知道AABB盒,然后把相机设置在AABB盒的中心,同时增加Size即可。
public List&Transform& CharactorL
& & void CreateCameraProjecterMatrix()
& && &&&Vector3 v3MaxPosition = -Vector3.one * f;
& && &&&Vector3 v3MinPosition = Vector3.one * f;& &
& && &&&for (int vertId = 0; vertId & CharactorList.C ++vertId)
& && && && &// Light view space
& && && && &Vector3 v3Position = camera1.worldToCameraMatrix.MultiplyPoint3x4(CharactorList[vertId].position);
& && && && &if (v3Position.x & v3MaxPosition.x)
& && && && &{
& && && && && & v3MaxPosition.x = v3Position.x;
& && && && &}
& && && && &if (v3Position.y & v3MaxPosition.y)
& && && && &{
& && && && && & v3MaxPosition.y = v3Position.y;
& && && && &}
& && && && &if (v3Position.z & v3MaxPosition.z)
& && && && &{
& && && && && & v3MaxPosition.z = v3Position.z;
& && && && &}
& && && && &if (v3Position.x & v3MinPosition.x)
& && && && &{
& && && && && & v3MinPosition.x = v3Position.x;
& && && && &}
& && && && &if (v3Position.y & v3MinPosition.y)
& && && && &{
& && && && && & v3MinPosition.y = v3Position.y;
& && && && &}
& && && && &if (v3Position.z & v3MinPosition.z)
& && && && &{
& && && && && & v3MinPosition.z = v3Position.z;
& && && && &}
& && &&&Vector3 off = v3MaxPosition - v3MinP
& && &&&Vector3 sizeOff =
& && &&&sizeOff.z = 0;
& && &&&float dis = sizeOff.
& && &&&//CreateOrthogonalProjectMatrix (ref m_projMatrix, v3MaxPosition, v3MinPosition);
& && &&&//Debug.Log (v3MaxPosition.ToString() + v3MinPosition.ToString());
& && &&&//Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1, 1, -1));
& && &&&//camera1.projectionMatrix = m * m_projM
& && &&&camera1.orthographicSize = dis / 1.8f;
& && &&&camera1.farClipPlane = off.z + 50;
& & void CreateViewMatrix(ref Matrix4x4 viewMatrix,Vector3 look,Vector3 up,Vector3 right,Vector3 pos)
& && &&&look.Normalize ();
& && &&&up.Normalize ();
& && &&&right.Normalize ();
& && &&&float x = -Vector3.Dot (right,pos);
& && &&&float y = -Vector3.Dot (up,pos);
& && &&&float z = -Vector3.Dot (look,pos);
& && &&&viewMatrix.m00 = right.x; viewMatrix.m10 = up.x; viewMatrix.m20 = look.x; viewMatrix.m30 = 0.0f;
& && &&&viewMatrix.m01 = right.y; viewMatrix.m11 = up.y; viewMatrix.m21 = look.y; viewMatrix.m31 = 0.0f;
& && &&&viewMatrix.m02 = right.z; viewMatrix.m12 = up.z; viewMatrix.m22 = look.z; viewMatrix.m32 = 0.0f;
& && &&&viewMatrix.m03 =& && & viewMatrix.m13 =& & viewMatrix.m23 =& && &viewMatrix.m33 = 1.0f;
& & void CreateOrthogonalProjectMatrix(ref Matrix4x4 projectMatrix,Vector3 v3MaxInViewSpace, Vector3 v3MinInViewSpace)
& && &&&float scaleX, scaleY, scaleZ;
& && &&&float offsetX, offsetY, offsetZ;
& && &&&scaleX = 2.0f / (v3MaxInViewSpace.x - v3MinInViewSpace.x);
& && &&&scaleY = 2.0f / (v3MaxInViewSpace.y - v3MinInViewSpace.y);
& && &&&offsetX = -0.5f * (v3MaxInViewSpace.x + v3MinInViewSpace.x) * scaleX;
& && &&&offsetY = -0.5f * (v3MaxInViewSpace.y + v3MinInViewSpace.y) * scaleY;
& && &&&scaleZ = 1.0f / (v3MaxInViewSpace.z - v3MinInViewSpace.z);
& && &&&offsetZ = -v3MinInViewSpace.z * scaleZ;
& && &&&//列矩阵
& && &&&projectMatrix.m00 = scaleX; projectMatrix.m01 = 0.0f; projectMatrix.m02 = 0.0f; projectMatrix.m03 = offsetX;
& && &&&projectMatrix.m10 = 0.0f; projectMatrix.m11 = scaleY; projectMatrix.m12 = 0.0f; projectMatrix.m13 = offsetY;
& && &&&projectMatrix.m20 = 0.0f; projectMatrix.m21 = 0.0f; projectMatrix.m22 = scaleZ; projectMatrix.m23 = offsetZ;
& && &&&projectMatrix.m30 = 0.0f; projectMatrix.m31 = 0.0f; projectMatrix.m32 = 0.0f; projectMatrix.m33 = 1.0f;
1.png (64.5 KB, 下载次数: 23)
16:42 上传
你看 所有角色都被包括在内了。当然具体适合的值你可以自己调整,这样我们就解决了第一个问题。 如果你的应用场景在室内,你可以无视第一个问题,直接手动设置一个
最合适的值就行了。
第二部,就是我们需要获得物体的剪影。就是说将物体的外轮廓给检录下来。当然复杂点就是获得物体的深度图。剪影获得很简单,我们看下深度图如何获得。因为在移动平台上不支持自动生成深度图,所以我打算自己使用片段着色器获得。
Shader &depthShader& {
& & Properties {
& & SubShader {
& &&&//Tags {&Queue&=&Transparent& &IgnoreProjector&=&True& &RenderType&=&Transparent&}
& && &&&Pass {
& && && && &CGPROGRAM
// Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without semantics (struct v2f members pos1)
#pragma exclude_renderers d3d11 xbox360
& && && && && & #pragma vertex vert
& && && && && & #pragma fragment frag
& && && && && & #include &UnityCG.cginc&
& && && && && &
& && && && && & sampler2D_float _CameraDepthT
& && && && && & struct appdata {
& && && && && && &&&float4 vertex : POSITION;
& && && && && && &&&
& && && && && & };
& && && && && & struct v2f {
& && && && && && &&&half4 pos : SV_POSITION;
& && && && && && &&&float2
& && && && && & };
& && && && && & v2f vert (appdata_base v) {
& && && && && && &&&v2
& && && && && && &&&o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
& && && && && && &&&o.depth = o.pos.
& && && && && && &&&
& && && && && & }
& && && && && &
& && && && && & fixed4 frag(v2f i) : COLOR
& && && && && & {
& && && && && && &&&float d = i.depth.x/i.depth.y;
& && && && && && &&&float flag = 0;
& && && && && && &&&if(d & 0)
& && && && && && &&&{
& && && && && && && && &d = abs(d);
& && && && && && && && &flag = 1;
& && && && && && &&&}
& && && && && && &&&float3 kEncodeMul = float3(1.0, 255.0, 65025.0);
& && && && && && &&&float kEncodeBit = 1.0/255.0;
& && && && && && &&&float3 enc = kEncodeMul *
& && && && && && &&&enc = frac (enc);
& && && && && && &&&enc -= enc.yzz * kEncodeB
& && && && && && &&&
& && && && && && &&&return fixed4(flag, enc);
& && && && && & }
& && && && &ENDCG
本来只要存储z就好了,不过很遗憾的是,有些平台的z竟然是负值,负值存储成像素是无意义的。所以我用r存储正负值,gba保存数值,但这样子性能确实会下降,毕竟在片段着色器里,考虑到后面需要照顾阴影质量,我觉得不使用深度图,而使用剪影。
剪影就简单了,连着色器都不用自己写,直接用摄像机渲染的黑图即可。这里我将摄像机设置成普通渲染模式,手动调用render,将贴图放到rendertexture中。
& && &&&camera1 = GameObject.Find (&Camera&).
& && &&&//camera1.hideFlags = HideFlags.HideAndDontS
& && &&&camera1.enabled =
& && &&&//camera1.projectionMatrix = camera.projectionM
& && &&&int textureSize = 1024;
& && &&&shadowTexture = new RenderTexture(textureSize , textureSize, 16, RenderTextureFormat.ARGB32);
& && &&&shadowTexture.name = &shadowTexture& + GetInstanceID();
& && &&&shadowTexture.isPowerOfTwo =
& && &&&shadowTexture.hideFlags = HideFlags.DontS
& && &&&camera1.targetTexture = shadowT
这样就可以看到如下贴图:
2.png (39.33 KB, 下载次数: 23)
16:42 上传
1024大小,内存6M 还算可以接受。当然,因为深度没有用,可以取消,这样会变成4M,其他格式可能会更小,但手机上不一定支持,所以暂时先这样吧。
第三个问题,就是怎么把这些东西投射到地上变成影子。
首先投射到地上已经有现成的Projector组件了,所以问题变成了坐标计算。
我们先把Projector的位置确定一下,Projector应该和主摄像机放在同一个地方,同时有同样的参数设置:
& && &&&proj = GameObject.Find (&GameObject&).GetComponent&Projector& ();
& && &&&proj.nearClipPlane = camera.nearClipP
& && &&&proj.farClipPlane = camera.farClipP
& && &&&proj.fieldOfView = camera.fieldOfV
这样就保证了视角内的物体都会出现阴影。然后就是坐标计算了,我们想一下,假设世界坐标中的点a,那么我们计算出它在正交摄像机中的坐标,然后根据坐标取出投影贴图中的点,那么假如这个点是全黑的(看你设置的是啥值了),那么就是不在阴影区的,假如不是,那么说明是阴影。
有了这个方案,我们就开始计算吧。首先我们可以轻易获得物体的坐标,问题在于怎么获得它在正交摄像机中的坐标,这个就需要使用正交摄像机的矩阵获得:
matVP = GL.GetGPUProjectionMatrix (camera1.projectionMatrix, true) * camera1.worldToCameraM
proj.material.SetMatrix(&ShadowMatrix&, matVP);
注意,我将这个计算出来的矩阵赋值给了一个材质,这个材质就是Projector使用的,因为是它需要根据坐标判断是否有阴影。
这样似乎接下来就可以直接写出着色器了:
& && && && &v2f vert (appdata_base v)
& && && && &{
& && && && && & v2
& && && && && & o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
& && && && && & float4x4 matWVP = mul (ShadowMatrix, _Object2World);
& && && && && & o.uvShadow = mul(matWVP, v.vertex);& && &&&
& && && && && &
& && && && &}
注意,顶点着色器不仅要计算出pos,同时还要获得正交相机中的坐标,也就是uvShadow.
然后就可以在片段着色器中处理了:
half2 uv = i.uvShadow.xy / i.uvShadow.w * 0.5 + 0.5;
& && && && && & #if UNITY_UV_STARTS_AT_TOP
& && && && && && &&&uv.y = 1 - uv.y;
& && && && && & #endif
& && && && && &&&fixed4 res = fixed4(0, 0, 0, 0);
& && && && && &&&fixed4 texS = tex2D(_ShadowTex, uv);
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a = 0.5;
& && && && && &&&}
就这么几行,阴影就出现了:
3.png (83.22 KB, 下载次数: 23)
16:42 上传
但效果似乎不那么尽如人意,让我们看下u3d自带的高品质的阴影效果:
4.png (83.41 KB, 下载次数: 22)
16:42 上传
其实已经很接近了呢,不过鉴于我开头宣称要高质量阴影,所以我打算继续优化边缘,因为你可以看到边缘部分的锯齿,当然我们可以单纯增加贴图大小,但是假如到2048,那么就要占据16M的内存了,所以我暂时打算用另外一种做法,pcf。就是通过采样,将边缘像素模糊化。
& && && && && & texS = tex2D(_ShadowTex, uv + half2(-0./pad, -0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
& && && && && &&&
& && && && && &&&texS = tex2D(_ShadowTex, uv + half2(0./pad, -0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
& && && && && &&&
& && && && && &&&texS = tex2D(_ShadowTex, uv + half2(-0./pad, -0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
& && && && && &&&texS = tex2D(_ShadowTex, uv + half2(0./pad, 0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
经过采样后效果如下:
5.png (89.53 KB, 下载次数: 23)
16:42 上传
最后 是时候做一次全面比较了,在电脑上我自己写的完爆自带的,因为我的电脑的显卡很渣,但手机显卡好一些,所以手机上的结果可能不太一样,放到手机上,开启最强模式,看看到底性能和效果对比吧。找了一台两年前的1000块钱的华为:
u3d 高质量阴影:
近距离效果:
6.jpg (506.71 KB, 下载次数: 28)
16:42 上传
再看我自己实现的效果:
2048最强模式下:
7.jpg (511.72 KB, 下载次数: 24)
16:42 上传
不过帧数下降了不少,再看看1024的吧。
8.jpg (514.65 KB, 下载次数: 23)
16:42 上传
效果也是要好上不少吧。
ok,暂时先这样吧
最后是源文件:
游客,本付费内容需要支付 2张CG券 才能下载
希望觉得有用的回复一下 顶一顶哈
感谢分享! 不觉明历!
非常的棒,感谢楼主分享的说
好好好好好好好好好
10万个赞!!!!
mark,感谢楼主分享,正在找这个东西
资源甚好,且阅且珍惜!
太感谢了!
喜子老师U3D高级班
掌握VR游戏制作就等于将前途放在了口袋中
【奖15张券】提高账户安全,完善个人资料!
Powered byUnity的实时阴影效率低吗_百度知道后使用快捷导航没有帐号?
只需一步,快速开始
查看: 2909|回复: 22
Unity实时高效阴影(实在没卷了,来赚一点点就好)
TA的其他好贴
马上注册,加入CGJOY,享用更多功能,让你轻松玩转CGJOY。
才可以下载或查看,没有帐号?
无意间看到一篇文章,说是Unity5 demo中为了实现角色的良好阴影,单独给角色设计了一个角色阴影系统。而且使用的是比较老的技术,但效果很好。其实在很多时候,我们需要的并不是万能的阴影光照系统,而是局部能做到效果就行。
& && &&&万能的好处在于任何情况都能看上去合理,但是相对的,性能开销也大,同时为了兼顾各种情况,只能做各种效果的折中,所以我们看到了现在移动平台上,要么就是没有实时阴影,要么就是充满锯齿的实时阴影,要么就是使用2D贴图来模拟实时阴影。
& && &&&用2D贴图来模拟的效果毫无疑问是最好的,但问题在于成本太高,很多小团队资金有限,很难专门为每一个角色都让美术画一大堆阴影贴图。而这也毫无疑问会增加游戏的大小。
& && &&&我主要思考的是,在某种条件下,是否可以实现局部的良好的阴影。比如角色展台,毫无疑问只会出现一个角色,那么这个情况下,毫无疑问我们需要的是一个完美的阴影。或者说某一些游戏,视角固定,而且能看到的范围很小,那么是否只针对这个部分去实现好的阴影系统。或者一个很小的室内,我们也需要一个好的角色阴影。
& && &&&ok,那么开始思考方案,首先我们应该只需要一个平行光的阴影。一般来说需要获得这个位置看过去的深度图。我首先在这个位置上放了一个正交摄像机,注意如果你想让角色有阴影,那么必须让角色处在这个正交摄像机的范围内,那么现在第一个问题来了,如何保证角色在正交摄像机的范围内?
& && &&&方法如下:首先你要获得你主摄像机内的所有的需要阴影的物体,然后将这些物体转化到正交摄像机的坐标中,计算出这些物体的最大范围,并得出正交矩阵赋值给正交摄像机。(代码借鉴了,对这个楼主深表感谢)
& && &&&这里要注意,Unity计算出来的Z是负值,但OpenGL是正的,官方说明如下:
Matrix that transforms from world to camera space.
Use this to calculate the camera space position of objects or to provide customcamera's location that is not based on the transform.
Note that camera space matches OpenGL convention: camera's forward is the negativeZ axis. This is different from Unity's convention, where forward is the positive Zaxis.
If you change this matrix, the camera no longer updates its rendering based on its Transform.This lasts until you call ResetWorldToCameraMatrix.
#pragma strict
// Offsets camera's rendering from the transform's position.
public var offset: Vector3 = new Vector3(0, 1, 0);
var camera: C
function Start() {
& & camera = GetComponent.&Camera&();
function LateUpdate() {
& & var camoffset: Vector3 = new Vector3(-offset.x, -offset.y, offset.z);
& & var m: Matrix4x4 = Matrix4x4.TRS(camoffset, Quaternion.identity, new Vector3(1, 1, -1));
& & camera.worldToCameraMatrix = m * transform.worldToLocalM
不过实际使用过程中,我们也许并不需要正确的矩阵赋值,因为你需要的是保证所有的物体在摄像机范围内,只需要知道AABB盒,然后把相机设置在AABB盒的中心,同时增加Size即可。
public List&Transform& CharactorL
& & void CreateCameraProjecterMatrix()
& && &&&Vector3 v3MaxPosition = -Vector3.one * f;
& && &&&Vector3 v3MinPosition = Vector3.one * f;& &
& && &&&for (int vertId = 0; vertId & CharactorList.C ++vertId)
& && && && &// Light view space
& && && && &Vector3 v3Position = camera1.worldToCameraMatrix.MultiplyPoint3x4(CharactorList[vertId].position);
& && && && &if (v3Position.x & v3MaxPosition.x)
& && && && &{
& && && && && & v3MaxPosition.x = v3Position.x;
& && && && &}
& && && && &if (v3Position.y & v3MaxPosition.y)
& && && && &{
& && && && && & v3MaxPosition.y = v3Position.y;
& && && && &}
& && && && &if (v3Position.z & v3MaxPosition.z)
& && && && &{
& && && && && & v3MaxPosition.z = v3Position.z;
& && && && &}
& && && && &if (v3Position.x & v3MinPosition.x)
& && && && &{
& && && && && & v3MinPosition.x = v3Position.x;
& && && && &}
& && && && &if (v3Position.y & v3MinPosition.y)
& && && && &{
& && && && && & v3MinPosition.y = v3Position.y;
& && && && &}
& && && && &if (v3Position.z & v3MinPosition.z)
& && && && &{
& && && && && & v3MinPosition.z = v3Position.z;
& && && && &}
& && &&&Vector3 off = v3MaxPosition - v3MinP
& && &&&Vector3 sizeOff =
& && &&&sizeOff.z = 0;
& && &&&float dis = sizeOff.
& && &&&//CreateOrthogonalProjectMatrix (ref m_projMatrix, v3MaxPosition, v3MinPosition);
& && &&&//Debug.Log (v3MaxPosition.ToString() + v3MinPosition.ToString());
& && &&&//Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(1, 1, -1));
& && &&&//camera1.projectionMatrix = m * m_projM
& && &&&camera1.orthographicSize = dis / 1.8f;
& && &&&camera1.farClipPlane = off.z + 50;
& & void CreateViewMatrix(ref Matrix4x4 viewMatrix,Vector3 look,Vector3 up,Vector3 right,Vector3 pos)
& && &&&look.Normalize ();
& && &&&up.Normalize ();
& && &&&right.Normalize ();
& && &&&float x = -Vector3.Dot (right,pos);
& && &&&float y = -Vector3.Dot (up,pos);
& && &&&float z = -Vector3.Dot (look,pos);
& && &&&viewMatrix.m00 = right.x; viewMatrix.m10 = up.x; viewMatrix.m20 = look.x; viewMatrix.m30 = 0.0f;
& && &&&viewMatrix.m01 = right.y; viewMatrix.m11 = up.y; viewMatrix.m21 = look.y; viewMatrix.m31 = 0.0f;
& && &&&viewMatrix.m02 = right.z; viewMatrix.m12 = up.z; viewMatrix.m22 = look.z; viewMatrix.m32 = 0.0f;
& && &&&viewMatrix.m03 =& && & viewMatrix.m13 =& & viewMatrix.m23 =& && &viewMatrix.m33 = 1.0f;
& & void CreateOrthogonalProjectMatrix(ref Matrix4x4 projectMatrix,Vector3 v3MaxInViewSpace, Vector3 v3MinInViewSpace)
& && &&&float scaleX, scaleY, scaleZ;
& && &&&float offsetX, offsetY, offsetZ;
& && &&&scaleX = 2.0f / (v3MaxInViewSpace.x - v3MinInViewSpace.x);
& && &&&scaleY = 2.0f / (v3MaxInViewSpace.y - v3MinInViewSpace.y);
& && &&&offsetX = -0.5f * (v3MaxInViewSpace.x + v3MinInViewSpace.x) * scaleX;
& && &&&offsetY = -0.5f * (v3MaxInViewSpace.y + v3MinInViewSpace.y) * scaleY;
& && &&&scaleZ = 1.0f / (v3MaxInViewSpace.z - v3MinInViewSpace.z);
& && &&&offsetZ = -v3MinInViewSpace.z * scaleZ;
& && &&&//列矩阵
& && &&&projectMatrix.m00 = scaleX; projectMatrix.m01 = 0.0f; projectMatrix.m02 = 0.0f; projectMatrix.m03 = offsetX;
& && &&&projectMatrix.m10 = 0.0f; projectMatrix.m11 = scaleY; projectMatrix.m12 = 0.0f; projectMatrix.m13 = offsetY;
& && &&&projectMatrix.m20 = 0.0f; projectMatrix.m21 = 0.0f; projectMatrix.m22 = scaleZ; projectMatrix.m23 = offsetZ;
& && &&&projectMatrix.m30 = 0.0f; projectMatrix.m31 = 0.0f; projectMatrix.m32 = 0.0f; projectMatrix.m33 = 1.0f;
1.png (64.5 KB, 下载次数: 23)
16:42 上传
你看 所有角色都被包括在内了。当然具体适合的值你可以自己调整,这样我们就解决了第一个问题。 如果你的应用场景在室内,你可以无视第一个问题,直接手动设置一个
最合适的值就行了。
第二部,就是我们需要获得物体的剪影。就是说将物体的外轮廓给检录下来。当然复杂点就是获得物体的深度图。剪影获得很简单,我们看下深度图如何获得。因为在移动平台上不支持自动生成深度图,所以我打算自己使用片段着色器获得。
Shader &depthShader& {
& & Properties {
& & SubShader {
& &&&//Tags {&Queue&=&Transparent& &IgnoreProjector&=&True& &RenderType&=&Transparent&}
& && &&&Pass {
& && && && &CGPROGRAM
// Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without semantics (struct v2f members pos1)
#pragma exclude_renderers d3d11 xbox360
& && && && && & #pragma vertex vert
& && && && && & #pragma fragment frag
& && && && && & #include &UnityCG.cginc&
& && && && && &
& && && && && & sampler2D_float _CameraDepthT
& && && && && & struct appdata {
& && && && && && &&&float4 vertex : POSITION;
& && && && && && &&&
& && && && && & };
& && && && && & struct v2f {
& && && && && && &&&half4 pos : SV_POSITION;
& && && && && && &&&float2
& && && && && & };
& && && && && & v2f vert (appdata_base v) {
& && && && && && &&&v2
& && && && && && &&&o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
& && && && && && &&&o.depth = o.pos.
& && && && && && &&&
& && && && && & }
& && && && && &
& && && && && & fixed4 frag(v2f i) : COLOR
& && && && && & {
& && && && && && &&&float d = i.depth.x/i.depth.y;
& && && && && && &&&float flag = 0;
& && && && && && &&&if(d & 0)
& && && && && && &&&{
& && && && && && && && &d = abs(d);
& && && && && && && && &flag = 1;
& && && && && && &&&}
& && && && && && &&&float3 kEncodeMul = float3(1.0, 255.0, 65025.0);
& && && && && && &&&float kEncodeBit = 1.0/255.0;
& && && && && && &&&float3 enc = kEncodeMul *
& && && && && && &&&enc = frac (enc);
& && && && && && &&&enc -= enc.yzz * kEncodeB
& && && && && && &&&
& && && && && && &&&return fixed4(flag, enc);
& && && && && & }
& && && && &ENDCG
本来只要存储z就好了,不过很遗憾的是,有些平台的z竟然是负值,负值存储成像素是无意义的。所以我用r存储正负值,gba保存数值,但这样子性能确实会下降,毕竟在片段着色器里,考虑到后面需要照顾阴影质量,我觉得不使用深度图,而使用剪影。
剪影就简单了,连着色器都不用自己写,直接用摄像机渲染的黑图即可。这里我将摄像机设置成普通渲染模式,手动调用render,将贴图放到rendertexture中。
& && &&&camera1 = GameObject.Find (&Camera&).
& && &&&//camera1.hideFlags = HideFlags.HideAndDontS
& && &&&camera1.enabled =
& && &&&//camera1.projectionMatrix = camera.projectionM
& && &&&int textureSize = 1024;
& && &&&shadowTexture = new RenderTexture(textureSize , textureSize, 16, RenderTextureFormat.ARGB32);
& && &&&shadowTexture.name = &shadowTexture& + GetInstanceID();
& && &&&shadowTexture.isPowerOfTwo =
& && &&&shadowTexture.hideFlags = HideFlags.DontS
& && &&&camera1.targetTexture = shadowT
这样就可以看到如下贴图:
2.png (39.33 KB, 下载次数: 23)
16:42 上传
1024大小,内存6M 还算可以接受。当然,因为深度没有用,可以取消,这样会变成4M,其他格式可能会更小,但手机上不一定支持,所以暂时先这样吧。
第三个问题,就是怎么把这些东西投射到地上变成影子。
首先投射到地上已经有现成的Projector组件了,所以问题变成了坐标计算。
我们先把Projector的位置确定一下,Projector应该和主摄像机放在同一个地方,同时有同样的参数设置:
& && &&&proj = GameObject.Find (&GameObject&).GetComponent&Projector& ();
& && &&&proj.nearClipPlane = camera.nearClipP
& && &&&proj.farClipPlane = camera.farClipP
& && &&&proj.fieldOfView = camera.fieldOfV
这样就保证了视角内的物体都会出现阴影。然后就是坐标计算了,我们想一下,假设世界坐标中的点a,那么我们计算出它在正交摄像机中的坐标,然后根据坐标取出投影贴图中的点,那么假如这个点是全黑的(看你设置的是啥值了),那么就是不在阴影区的,假如不是,那么说明是阴影。
有了这个方案,我们就开始计算吧。首先我们可以轻易获得物体的坐标,问题在于怎么获得它在正交摄像机中的坐标,这个就需要使用正交摄像机的矩阵获得:
matVP = GL.GetGPUProjectionMatrix (camera1.projectionMatrix, true) * camera1.worldToCameraM
proj.material.SetMatrix(&ShadowMatrix&, matVP);
注意,我将这个计算出来的矩阵赋值给了一个材质,这个材质就是Projector使用的,因为是它需要根据坐标判断是否有阴影。
这样似乎接下来就可以直接写出着色器了:
& && && && &v2f vert (appdata_base v)
& && && && &{
& && && && && & v2
& && && && && & o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
& && && && && & float4x4 matWVP = mul (ShadowMatrix, _Object2World);
& && && && && & o.uvShadow = mul(matWVP, v.vertex);& && &&&
& && && && && &
& && && && &}
注意,顶点着色器不仅要计算出pos,同时还要获得正交相机中的坐标,也就是uvShadow.
然后就可以在片段着色器中处理了:
half2 uv = i.uvShadow.xy / i.uvShadow.w * 0.5 + 0.5;
& && && && && & #if UNITY_UV_STARTS_AT_TOP
& && && && && && &&&uv.y = 1 - uv.y;
& && && && && & #endif
& && && && && &&&fixed4 res = fixed4(0, 0, 0, 0);
& && && && && &&&fixed4 texS = tex2D(_ShadowTex, uv);
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a = 0.5;
& && && && && &&&}
就这么几行,阴影就出现了:
3.png (83.22 KB, 下载次数: 23)
16:42 上传
但效果似乎不那么尽如人意,让我们看下u3d自带的高品质的阴影效果:
4.png (83.41 KB, 下载次数: 22)
16:42 上传
其实已经很接近了呢,不过鉴于我开头宣称要高质量阴影,所以我打算继续优化边缘,因为你可以看到边缘部分的锯齿,当然我们可以单纯增加贴图大小,但是假如到2048,那么就要占据16M的内存了,所以我暂时打算用另外一种做法,pcf。就是通过采样,将边缘像素模糊化。
& && && && && & texS = tex2D(_ShadowTex, uv + half2(-0./pad, -0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
& && && && && &&&
& && && && && &&&texS = tex2D(_ShadowTex, uv + half2(0./pad, -0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
& && && && && &&&
& && && && && &&&texS = tex2D(_ShadowTex, uv + half2(-0./pad, -0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
& && && && && &&&texS = tex2D(_ShadowTex, uv + half2(0./pad, 0./pad));
& && && && && &&&if(texS.a & 0)
& && && && && &&&{
& && && && && && && &res.a += _S
& && && && && &&&}
经过采样后效果如下:
5.png (89.53 KB, 下载次数: 23)
16:42 上传
最后 是时候做一次全面比较了,在电脑上我自己写的完爆自带的,因为我的电脑的显卡很渣,但手机显卡好一些,所以手机上的结果可能不太一样,放到手机上,开启最强模式,看看到底性能和效果对比吧。找了一台两年前的1000块钱的华为:
u3d 高质量阴影:
近距离效果:
6.jpg (506.71 KB, 下载次数: 28)
16:42 上传
再看我自己实现的效果:
2048最强模式下:
7.jpg (511.72 KB, 下载次数: 24)
16:42 上传
不过帧数下降了不少,再看看1024的吧。
8.jpg (514.65 KB, 下载次数: 23)
16:42 上传
效果也是要好上不少吧。
ok,暂时先这样吧
最后是源文件:
游客,本付费内容需要支付 2张CG券 才能下载
希望觉得有用的回复一下 顶一顶哈
感谢分享! 不觉明历!
非常的棒,感谢楼主分享的说
好好好好好好好好好
10万个赞!!!!
mark,感谢楼主分享,正在找这个东西
资源甚好,且阅且珍惜!
太感谢了!
喜子老师U3D高级班
掌握VR游戏制作就等于将前途放在了口袋中
【奖15张券】提高账户安全,完善个人资料!
Powered by

我要回帖

更多关于 unity移动端实时阴影 的文章

 

随机推荐