模型勾边outline

简述
在卡通渲染中一般会涉及到模型勾边。效率最高的方式是在shader中去做。render to texture的实现方式这里不讨论。

Shader勾边实现流程大致为:对模型进行2遍(2个pass)绘制,第一遍(勾边pass)在vertex shader中对模型沿顶点法线方向放大,fragment shader设置输出颜色为勾边颜色;第二遍正常绘制模型,除被放大的部分外,其余被覆盖,这样就有了勾边的效果。

实现
SubShader 
{
    Tags { "RenderType"="Opaque"}

    pass
    { 
        ZWrite Off

        CGPROGRAM
           #include "UnityCG.cginc" 

        struct v2f_outline {
            float4 pos : SV_POSITION;
        };          

        v2f_outline vert_outline(appdata_full v) {
            // vertex data scaled according to normal direction
            v2f_outline o;
            v.vertex.xyz += v.normal*0.01;

            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);        
            return o;
        }
        half4 frag_outline( v2f_outline i) :COLOR 
        {
            return half4(0, 1, 0, 1);
        }

        #pragma vertex vert_outline
        #pragma fragment frag_outline
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }

    Pass 
    {
        CGPROGRAM

        v2f_full vert (appdata_full v) 
        {
            v2f_full o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);           

            return o; 
        }

        fixed4 frag (v2f_full i) : COLOR0 
        {                       
            fixed4 tex = tex2D (_MainTex, i.uv.xy);         
            return tex;     
        }   

        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }
}


其中需要注意的是勾边pass的ZWrite设为关闭,保证接下来的正常绘制不会因为深度检测被剔除。 


改进
1.如何保证勾边在不同的摄像机距离下大小一致?

之前实现的放大绘制是在模型空间下做的,这使得勾边跟模型一样存在近大远小的问题,远处模型的勾边会比近处模型的勾边细,所以我们需要对勾边根据离摄像机的远近进行一个缩放。

v2f_outline vert_outline(appdata_full v) {
    // vertex data scaled according to normal direction
    v2f_outline o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    float3 norm   = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
    float3 offset = TransformViewToProjection(normalize(norm.xyz));     
    o.pos.xy += normalize(offset) * 0.01 * o.pos.w;         

    return o;
}

模型放大放到裁剪空间去做,o.pos存错的是裁剪空间的顶点信息,其中o.pos.w存储了顶点与摄像机的距离,可做为一个模型放大系数。 


2.如何解决在与其他模型重叠的情况下出现不合理的勾边?


某个摄像机距离下重叠的边界出现勾边,移动摄像机勾边交替闪烁,时有时无,这是z-fighting现象,解决的办法是设置勾边pass的Offset语句。

pass
{ 
    Offset 3, 0
    ZWrite Off
    ...


3.如何在被遮挡的情况下显示勾边?
物体被遮挡,仍然需要显示,需要设置勾边pass的语句ZTest为Always,深度检测一直通过,这样勾边pass的片段不会被深度剔除。

pass
{ 
    Offset 3, 0
    ZWrite Off
    ZTest Always
    ...



在这个基础上如果只需显示被遮挡的勾边,就需要利用Stencil Buffer。绘制顺序需要反过来:先执行正常绘制,写入stencil值,然后执行勾边pass,对stencil的值做比较,如果相等,则片段被stencil剔除,这样,除了放大的部分外,其余部分都被剔除了。

SubShader 
{
    Tags { "RenderType"="Opaque"}

    Pass 
    {           
        Stencil {
            Ref 2
            Comp always
            Pass replace
            ZFail replace
        }
        CGPROGRAM

        v2f_full vert (appdata_full v) 
        {
            v2f_full o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);           

            return o; 
        }

        fixed4 frag (v2f_full i) : COLOR0 
        {                       
            fixed4 tex = tex2D (_MainTex, i.uv.xy);         
            return tex;     
        }   

        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }

    pass
    { 
         Stencil {
             Ref 2
             Comp NotEqual
         }

        Offset 3, 0
        ZWrite Off
        ZTest Always

        CGPROGRAM
        #include "UnityCG.cginc"    

        struct v2f_outline {
            float4 pos : SV_POSITION;
        };          

        v2f_outline vert_outline(appdata_full v) {
            // vertex data scaled according to normal direction
            v2f_outline o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            float3 norm   = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
            float3 offset = TransformViewToProjection(normalize(norm.xyz));     
            o.pos.xy += normalize(offset) * 0.01 * o.pos.w;         

            return o;
        }

        half4 frag_outline( v2f_outline i) :COLOR 
        {
            return half4(0, 1, 0, 1);
        }

        #pragma vertex vert_outline
        #pragma fragment frag_outline
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }
}


--------------------- 
作者:bill2ccssddnn 
来源:CSDN 
原文:https://blog.csdn.net/bill2ccssddnn/article/details/52148647 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

内容概要:本文系统阐述了正规表达式(正则表达式)作为词法分析核心工具的理论基础与实际应用。文章从字母表、符号串等基本概念出发,详细介绍了正规式的定义、运算规则、代数性质及其与有限自动机(NFA/DFA)的等价关系,阐明了通过Thompson构造法、子集构造法和DFA最小化实现词法分析器自动生成的技术路径。同时,文中列举了标识符、关键字、运算符等编程语言元素的正规式描述,并说明了最长匹配和优先级规则在歧义消解中的作用。此外,还对比了正规式与上下文无关文法的表达能力差异,指出了其在嵌套结构和计数能力上的局限性,并介绍了Lex/Flex等词法分析器生成工具的应用场景。; 适合人群:计算机相关专业学生、编译原理初学者、希望深入理解词法分析机制的研发人员;具备一定的离散数学和形式语言基础者更佳。; 使用场景及目标:① 学习如何使用正规表达式精确描述程序语言的词法规则;② 掌握从正规式到DFA的转换流程及其实现原理,为构建编译器前端打下基础;③ 理解词法分析器生成工具的工作机制,提升对自动化工具的理解与运用能力。; 阅读建议:建议结合编译器设计实践进行学习,尝试手动完成正规式到NFA再到DFA的转换练习,并使用Flex等工具验证结果,以加深对理论知识的理解与应用。
内容概要:本文针对高比例清洁能源接入背景下配电网重构的关键问题,提出了一种计及需求响应机制的优化方法,并以IEEE33节点标准系统为案例,采用Matlab进行建模、仿真与代码实现。研究通过构建综合考虑风电、光伏等间歇性电源出力不确定性和负荷波动的数学模型,引入智能优化算法求解网络重构方案,有效提升了配电网对清洁能源的接纳能力与运行的经济性、可靠性。文中重点探讨了需求响应在削峰填谷、平抑波动方面的作用,实现了网络结构的动态优化调整,为新型电力系统下的主动配电网管理提供了可行的技术路径和仿真验证平台。该工作属于电力系统智能优化领域的前沿实践,具有较强的理论价值与工程应用前景。; 适合人群:具备电力系统分析、优化算法基础及Matlab编程能力,从事新能源并网、智能电网、需求响应等相关方向研究的研究生、科研人员及电力行业工程技术人员。; 使用场景及目标:①应用于高渗透率可再生能源接入的配电网运行优化研究;②为需求响应参与下的网络重构问题提供完整的算法设计与Matlab仿真解决方案;③服务于IEEE标准测试系统的教学演示、科研验证及算法对比分析。; 阅读建议:建议读者结合提供的Matlab代码深入理解模型构建与求解流程,动手运行仿真程序并尝试调整参数,以掌握不同场景下重构策略的变化规律,同时可进一步拓展至多时段动态重构、加入储能系统或电动汽车等新型负荷进行深化研究。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值