美文网首页
使用顶点投射的方法制作实时阴影(转)

使用顶点投射的方法制作实时阴影(转)

作者: 雄关漫道从头越 | 来源:发表于2019-06-15 11:14 被阅读0次

使用顶点投射的方法制作实时阴影
Unity平面阴影(王者荣耀阴影实现)
实时阴影技术

写在前面

阴影是计算机图形学中一个很重要的部分,阴影的加入使得物体更加具有立体感,也有助于我们理解物体间的相互位置关系和大小。

实时阴影的实现方法有很多种,shadowMap适用性最好,但性能开销也大,有时候我们的项目其实并不需要那么通用的阴影,我们只需要一个“适用某些特定场合”的一个“看起来正确”的实时阴影。

本文所说的,就是一种利用顶点投射的方法实现的实时阴影技术,在一些阴影质量要求不高,地面平整的项目是一个非常合适的方案,现在很火的手游《王者荣耀》就用了类似的技术。

(经提醒,这个技术叫平面投影阴影(Planar Projected Shadows)技术,由Jim Blinn 1988年提出。http://www.twinklingstar.cn/2015/1717/tech-of-shadows/#21_Blinns)

原理

忽略自身阴影不谈,如果我们只考虑物体在地面上的阴影的话,其实就可以把这个问题简单概括为求一个物体每一个顶点在某个平面上的投影位置了。

公式推导

说到求投影,我当时首先想到的就是点积,后来经群友提醒,其实可以更进一步简化为求相似三角形,这样理解起来似乎还更简单些,以下是推导过程,为了简化计算,我们在二维空间内进行推导。

根据已知条件,我们可以得到一个这样的题目:已知平面坐标系内一个单位向量L(Lx,Ly),坐标系内一点M(Mx,My),求点M沿着L方向 在y = h上的投影位置P,如下图所示:

根据相似三角形定理,我们很容易可以得出下面的式子:

于是我们有:

在shader中实现

有了公式以后,剩下的就简单了,我们只需要在shader中多写一个pass,在这个pass中把所有的顶点移动到投影的位置进行渲染即可,注意要转换到世界空间中进行计算,核心代码如下:

//阴影pass
Pass
{
    Name "Shadow"

    //用使用模板测试以保证alpha显示正确
    Stencil
    {
        Ref 0
        Comp equal
        Pass incrWrap
        Fail keep
        ZFail keep
    }

    //透明混合模式
    Blend SrcAlpha OneMinusSrcAlpha

    //关闭深度写入
    ZWrite off

    //深度稍微偏移防止阴影与地面穿插
    Offset -1 , 0

    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag

    #include "UnityCG.cginc"
    struct appdata
    {
        float4 vertex : POSITION;
    };

    struct v2f
    {
        float4 vertex : SV_POSITION;
        float4 color : COLOR;
    };

    float4 _LightDir;
    float4 _ShadowColor;
    float _ShadowFalloff;
    float _ShadowY;//地面高度

    float3 ShadowProjectPos(float4 vertPos)
    {
        float3 shadowPos;

        //得到顶点的世界空间坐标
        float3 worldPos = mul(unity_ObjectToWorld , vertPos).xyz;

        //灯光方向
        float3 lightDir = normalize(_LightDir.xyz);

        //阴影的世界空间坐标(低于地面的部分不做改变)
        shadowPos.y = min(worldPos.y , _ShadowY);
        shadowPos.xz = worldPos.xz - lightDir.xz * max(0 , worldPos.y - _ShadowY) / lightDir.y; 

        return shadowPos;
    }

    v2f vert (appdata v)
    {
        v2f o;

        //得到阴影的世界空间坐标
        float3 shadowPos = ShadowProjectPos(v.vertex);

        //转换到裁切空间
        o.vertex = UnityWorldToClipPos(shadowPos);

        //得到中心点世界坐标
        float3 center =float3(unity_ObjectToWorld[0].w , _ShadowY , unity_ObjectToWorld[2].w);
        //计算阴影衰减
        float falloff = 1-saturate(distance(shadowPos , center) * _ShadowFalloff);

        //阴影颜色
        o.color = _ShadowColor; 
        o.color.a *= falloff;

        return o;
    }

    fixed4 frag (v2f i) : SV_Target
    {
        return i.color;
    }
    ENDCG
}

其中_LightDir.xyz是灯光方向,_LightDir.w是地面高度,_ShadowColor为阴影颜色,这几个值可以设一个全局变量对场景中的所有物体统一赋值。

重叠的面会导致透明混合结果错误,用Stencil解决,最后用顶点和中心点的距离算一个阴影衰减

最终效果如下:

相关文章

  • 使用顶点投射的方法制作实时阴影(转)

    使用顶点投射的方法制作实时阴影Unity平面阴影(王者荣耀阴影实现)实时阴影技术 写在前面 阴影是计算机图形学中一...

  • PS阴影总分类

    参考阴影教程,超详细的各种阴影的制作方法 目录 一、接触阴影二、柔和阴影三、投射阴影 一、接触阴影 接触阴影就是物...

  • 结合Projector和Rendertexture实现实时阴影及

    说到实时阴影的实现,一般比较容易想到使用ShadowMap,通过投射灯光空间的深度图,并在投射物体上进行深度比较,...

  • Unity图形渲染的优化

    移动平台实战优化 顶点数控制在80k以下最多使用一盏实时光不用阴影尽量少用粒子系统,用其他的方法代替,发射器数量3...

  • PS阴影三:投射阴影[转]

    如下图演示,复制大白图层,然后用“色相/饱和度”命令将明度降低到零,然后变换命令进行翻转\位置调整和变形,再加点高...

  • Android之圆形预览的SurfaceView

    使用ViewOutlineProvider来实现,ViewOutlineProvider专门用于阴影投射和剪切。 ...

  • vray 渲染阴影层方法

    单独渲染阴影层:阴影投射方式有两种 第一种:物体投射到非本身上面。就是投射阴影的物体和接受阴影的物体互不影响。 1...

  • 实时阴影

    Shader "Custom/SingleShadow" { Properties { _MainTi...

  • Unity3D CustomSRP[译].6.阴影遮罩[Shad

    Shadow Masks(阴影遮罩) ——烘焙直接光遮罩 本节内容 烘焙静态阴影 合并实时光照和烘焙阴影 混合实时...

  • 别人身上有的,其实你也有

    外在的一切都是我们内在的投射。 投射有“阴影投射”,也有“黄金投射”. 阴影投射是指内在一些我们不承认,很排斥又或...

网友评论

      本文标题:使用顶点投射的方法制作实时阴影(转)

      本文链接:https://www.haomeiwen.com/subject/eqgvfctx.html