博客
关于我
【Shader入门精要】第十四章——卡通风格的渲染
阅读量:500 次
发布时间:2019-03-07

本文共 9130 字,大约阅读时间需要 30 分钟。

卡通风格渲染与素描风格渲染技术实现

一、卡通风格渲染

卡通风格的渲染通常采用渐变纹理漫反射和分块的高光反射处理,能够在视觉上创造出生动的卡通效果。这种方法通过模拟光线反射和漫反射,赋予物体颜色和质感,使其更加生动且富有层次感。

渲染过程

  • 轮廓线处理

    • 使用法线扩展技术,向外扩展并渲染背面,生成轮廓线。
    • 渲染结果为边缘颜色,用于后续细节处理。
  • 漫反射渐变纹理

    • 采用渐变纹理漫反射,通过纹理映射来模拟光线在表面产生的漫反射效果。
    • 使用光照方向和法线方向的点积来计算纹理坐标,采样纹理数据生成漫反射颜色。
  • 高光反射处理

    • 高光反射采用分块处理,通过设置阈值来控制反光区域。
    • 阈值的选择决定了反光块的大小和数量,避免锯齿现象,实现平滑的高光效果。
  • 光照衰减与抗锯齿

    • 通过计算光照衰减值,结合法线方向和视角方向,调整高光反射的强度。
    • 使用动态阈值和平滑步进函数(smoothstep),实现高光反射的平滑过渡。
  • 代码实现

    Shader "MilkShader/14/ToonShading" {    Properties {        _Color("Color Tint", Color) = (1,1,1,1)        _MainTex("Main Tex", 2D) = "white"        _Ramp("Ramp Texture", 2D) = "white"        _Outline("Outline", Range(0,1)) = 0.1        _OutlineColor("Outline Color", Color) = (1,1,1,1)        _Specular("Specular", Color) = (1,1,1,1)        _SpecularScale("Specular Scale", Range(0, 0.1)) = 0.01    }    SubShader {        Tags {"RenderType"="Opaque" "Queue"="Geometry"}        LOD 100        Pass {            NAME "OUTLINE"            Cull Front            CGPROGRAM            #pragma vertex vert            #pragma fragment frag            include "UnityCG.cginc"            float _Outline;            fixed4 _OutlineColor;            struct a2v {                float4 vertex : POSITION;                float3 normal : NORMAL;            };            struct v2f {                float4 pos : SV_POSITION;            };            v2f vert(a2v v) {                v2f o;                pos = mul(UNITY_MATRIX_MV, v.vertex);                normal = mul(UNITY_MATRIX_IT_MV, v.normal);                normal.z = -0.5;                pos = pos + normalize(normal) * _Outline;                o.pos = mul(UNITY_MATRIX_P, pos);                return o;            }            float4 frag(v2f i) : SV_Target {                return _OutlineColor.rgb;            }            ENDCG        }        Pass {            Tags {"LightMode"="ForwardBase"}            Cull Back            CGPROGRAM            include "UnityCG.cginc"            include "Lighting.cginc"            include "AutoLight.cginc"            include "UnityShaderVariables.cginc"            #pragma vertex vert            #pragma fragment frag            #pragma multi_compile_fwdbase            sampler2D _MainTex;            float4 _MainTex_ST;            fixed4 _Color;            sampler2D _Ramp;            fixed4 _Specular;            fixed _SpecularScale;            struct a2v {                float4 vertex : POSITION;                float3 normal : NORMAL;                float4 texcoord : TEXCOORD0;            };            struct v2f {                float4 pos : POSITION;                float2 uv : TEXCOORD0;                float3 worldNormal : TEXCOORD1;                float3 worldPos : TEXCOORD2;                SHADOW_COORDS(3)            };            v2f vert(a2v v) {                v2f o;                o.pos = UnityObjectToClipPos(v.vertex);                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);                o.worldNormal = UnityObjectToWorldNormal(v.normal);                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;                TRANSFER_SHADOW(o);                return o;            }            float4 frag(v2f i) : SV_Target {                fixed3 worldNormal = normalize(i.worldNormal);                fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));                fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));                fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir);                fixed4 c = tex2D(_MainTex, i.uv);                fixed3 albedo = c.rgb * _Color.rgb;                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);                fixed diff = dot(worldNormal, worldLightDir);                diff = (diff * 0.5 + 0.5) * atten;                fixed3 diffuse = _LightColor0.rgb * albedo * tex2D(_Ramp, float2(diff, diff)).rgb;                fixed spec = dot(worldNormal, worldHalfDir);                fixed w = fwidth(spec) * 2.0;                fixed3 specular = _Specular.rgb * lerp(0,1, smoothstep(-w,w,spec + _SpecularScale - 1)) * step(0.0001, _SpecularScale);                return fixed4(ambient + diffuse + specular, 1.0);            }            ENDCG        }    }    Fallback "Diffuse"}

    二、素描风格渲染

    素描风格的渲染通过多层纹理和线条处理,模拟手绘效果,赋予物体细腻的艺术感。这种方法通过层叠纹理和权重计算,生成丰富的纹理效果。

    渲染过程

  • 素描纹理层叠

    • 使用多个素描纹理层(如Hatch0到Hatch5),每层负责特定的纹理线条生成。
    • 通过顶点着色器计算每个纹理层的权重,决定其对最终颜色的影响程度。
  • 纹理权重计算

    • 根据法线方向和光照方向,计算纹理权重,确定哪些纹理层对当前片元有影响。
    • 权重计算分为不同的区间,每个区间对应不同的纹理层和权重分配。
  • 颜色混合

    • 在片元着色器中,根据权重对不同纹理层的颜色进行加权混合。
    • 非素描部分使用白色作为基色,素描部分根据权重计算颜色。
  • 光照处理

    • 结合光照衰减值,调整纹理颜色,确保光照和阴影效果自然融合。
  • 代码实现

    Shader "MilkShader/14/Hatching" {    Properties {        _Color("Color Tint", Color) = (1,1,1,1)        _TileFactor("Tile Factor", Float) = 1        _Outline("Outline", Range(0,1)) = 0.1        _OutlineColor("Outline Color", Color) = (1,1,1,1)        _Hatch0("Hatch 0", 2D) = "white"        _Hatch1("Hatch 1", 2D) = "white"        _Hatch2("Hatch 2", 2D) = "white"        _Hatch3("Hatch 3", 2D) = "white"        _Hatch4("Hatch 4", 2D) = "white"        _Hatch5("Hatch 5", 2D) = "white"    }    SubShader {        Tags {"RenderType"="Opaque" "Queue"="Geometry"}        UsePass "MilkShader/14/ToonShading/OUTLINE"        Pass {            Tags {"LightMode"="ForwardBase"}            CGPROGRAM            #pragma vertex vert            #pragma fragment frag            #pragma multi_compile_fwdbase            include "UnityCG.cginc"            include "Lighting.cginc"            include "AutoLight.cginc"            include "UnityShaderVariables.cginc"            fixed4 _Color;            fixed _TileFactor;            fixed _Outline;            fixed4 _OutlineColor;            sampler2D _Hatch0;            sampler2D _Hatch1;            sampler2D _Hatch2;            sampler2D _Hatch3;            sampler2D _Hatch4;            sampler2D _Hatch5;            struct appdata {                float4 vertex : POSITION;                float4 tangent : TANGENT;                float2 texcoord : TEXCOORD0;                float3 normal : NORMAL;            };            struct v2f {                float4 pos : POSITION;                float2 uv : TEXCOORD0;                fixed3 hatchWeight0 : TEXCOORD1;                fixed3 hatchWeight1 : TEXCOORD2;                float3 worldPos : TEXCOORD3;                SHADOW_COORDS(4)            };            v2f vert(appdata v) {                v2f o;                o.pos = UnityObjectToClipPos(v.vertex);                o.uv = v.texcoord.xy * _TileFactor;                fixed3 worldNormal = normalize(UnityObjectToWorldNormal(v.normal));                fixed3 worldLightDir = normalize(WorldSpaceLightDir(v.vertex));                fixed diff = max(0, dot(worldNormal, worldLightDir));                hatchFactor = diff * 7.0;                o.hatchWeight0 = fixed3(0,0,0);                o.hatchWeight1 = fixed3(0,0,0);                if (hatchFactor > 6.0) {                } else if (hatchFactor > 5.0) {                    o.hatchWeight0.x = hatchFactor - 5.0;                } else if (hatchFactor > 4.0) {                    o.hatchWeight0.x = hatchFactor - 4.0;                    o.hatchWeight0.y = 1 - o.hatchWeight0.x;                } else if (hatchFactor > 3.0) {                    o.hatchWeight0.y = hatchFactor - 3.0;                    o.hatchWeight0.z = 1 - o.hatchWeight0.y;                } else if (hatchFactor > 2.0) {                    o.hatchWeight0.z = hatchFactor - 2.0;                    o.hatchWeight1.x = 1 - o.hatchWeight0.z;                } else if (hatchFactor > 1.0) {                    o.hatchWeight1.x = hatchFactor - 1.0;                    o.hatchWeight1.y = 1 - o.hatchWeight1.x;                } else {                    o.hatchWeight1.y = hatchFactor;                    o.hatchWeight1.z = 1 - o.hatchWeight1.y;                }                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;                TRANSFER_SHADOW(o);                return o;            }            fixed4 frag(v2f i) : SV_Target {                fixed4 hatchTex0 = tex2D(_Hatch0, i.uv) * i.hatchWeight0.x;                fixed4 hatchTex1 = tex2D(_Hatch1, i.uv) * i.hatchWeight0.y;                fixed4 hatchTex2 = tex2D(_Hatch2, i.uv) * i.hatchWeight0.z;                fixed4 hatchTex3 = tex2D(_Hatch3, i.uv) * i.hatchWeight1.x;                fixed4 hatchTex4 = tex2D(_Hatch4, i.uv) * i.hatchWeight1.y;                fixed4 hatchTex5 = tex2D(_Hatch5, i.uv) * i.hatchWeight1.z;                fixed4 whiteColor = fixed4(1,1,1,1) * (1 - i.hatchWeight0.x - i.hatchWeight0.y - i.hatchWeight0.z - i.hatchWeight1.x - i.hatchWeight1.y - i.hatchWeight1.z);                fixed4 hatchColor = hatchTex0 + hatchTex1 + hatchTex2 + hatchTex3 + hatchTex4 + hatchTex5 + whiteColor;                UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);                return fixed4(hatchColor.rgb * _Color.rgb * atten, 1.0);            }            ENDCG        }    }    Fallback "Diffuse"}

    平铺系数说明

    平铺系数(_TileFactor)决定了纹理的密度。值越大,纹理线条越密集;值越小,纹理线条越稀疏。通过调整平铺系数,可以根据需求控制纹理的细腻程度。

    总结

    卡通风格和素描风格渲染通过不同的纹理处理和光照模拟方法,分别实现了生动的卡通效果和细腻的素描效果。两种方法都结合了光照和纹理的精细控制,确保了视觉效果的自然和一致性。

    转载地址:http://xhucz.baihongyu.com/

    你可能感兴趣的文章
    mysql 主从互备份_mysql互为主从实战设置详解及自动化备份(Centos7.2)
    查看>>
    mysql 主从关系切换
    查看>>
    MYSQL 主从同步文档的大坑
    查看>>
    mysql 主键重复则覆盖_数据库主键不能重复
    查看>>
    Mysql 事务知识点与优化建议
    查看>>
    Mysql 优化 or
    查看>>
    mysql 优化器 key_mysql – 选择*和查询优化器
    查看>>
    MySQL 优化:Explain 执行计划详解
    查看>>
    Mysql 会导致锁表的语法
    查看>>
    mysql 使用sql文件恢复数据库
    查看>>
    mysql 修改默认字符集为utf8
    查看>>
    Mysql 共享锁
    查看>>
    MySQL 内核深度优化
    查看>>
    mysql 内连接、自然连接、外连接的区别
    查看>>
    mysql 写入慢优化
    查看>>
    mysql 分组统计SQL语句
    查看>>
    Mysql 分页
    查看>>
    Mysql 分页语句 Limit原理
    查看>>
    MySql 创建函数 Error Code : 1418
    查看>>
    MySQL 创建新用户及授予权限的完整流程
    查看>>