Exemplos Práticos de Shaders de Fragmento no Unity

Este exemplo demonstra como integrar diferentes componentes de iluminação em um único shader de fragmento. O shader calcula a luz ambiente, reflexão difusa, brilho especular e até quatro luzes pontuais simultaneamente.

Shader "Tutorial/FragmentoIluminacao" {
    properties
    {
        _DiffuseTint("Cor Difusa",color)=(1,1,1,1)
        _HighlightColor("Cor Especular",color)=(1,1,1,1)
        _HighlightPower("Potência Especular",range(1,32))=8
    }
    Subshader
    {
        pass{
            tags{"LightMode"="Forwardbase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
            #include "lighting.cginc"

            float4 _DiffuseTint;
            float4 _HighlightColor;
            float _HighlightPower;

            struct Interpolators
            {
                float4 clipPos:POSITION;
                float3 worldNormal:TEXCOORD1;
                float3 objPos:COLOR;
            };


            Interpolators vert(appdata_base v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=v.normal;
                o.objPos=v.vertex;
                return o;
            }
            float4 frag(Interpolators IN):COLOR
            {
                //Luz ambiente global
                fixed4 finalColor=UNITY_LIGHTMODEL_AMBIENT;

                //Componente difusa - produto escalar entre normal e direção da luz
                float3 normalWorld=UnityObjectToWorldNormal(IN.worldNormal);
                float3 lightDir=normalize(WorldSpaceLightDir(IN.objPos));
                float diffuseFactor=saturate(dot(normalWorld,lightDir));
                finalColor+=_LightColor0*_DiffuseTint*diffuseFactor;

                //Componente especular - reflexão da luz
                float3 viewDir=normalize(WorldSpaceViewDir(IN.objPos));
                float3 reflectionDir=2*dot(normalWorld,lightDir)*normalWorld-lightDir;
                float specularFactor=saturate(dot(reflectionDir,viewDir));
                finalColor+=_HighlightColor*pow(specularFactor,_HighlightPower);

                //Contribuição das luzes pontuais
                float3 worldPosition=mul(unity_ObjectToWorld,IN.objPos).xyz;
                finalColor.rgb+=Shade4PointLights
                (
                    unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
                    unity_LightColor[0].rgb,unity_LightColor[1].rgb,unity_LightColor[2].rgb,unity_LightColor[3].rgb,
                    unity_4LightAtten0,
                    worldPosition,normalWorld
                );
                return finalColor;
            }

            ENDCG
        }
    }
}

  1. Suporte a Sombras Projetadas

Para habilitar sombras em um shader personalizado, uma abordagem prática é utilizar o fallback para o shader padrão Diffuse, que já implementa a geração de sombras para luzes direcionais e pontuais.

Shader "Tutorial/FragmentoComSombras" {
    properties
    {
        _DiffuseTint("Cor Difusa",color)=(1,1,1,1)
        _HighlightColor("Cor Especular",color)=(1,1,1,1)
        _HighlightPower("Potência Especular",range(1,32))=8
    }
    Subshader
    {
        pass{
            tags{"LightMode"="Forwardbase"}

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
            #include "lighting.cginc"

            float4 _DiffuseTint;
            float4 _HighlightColor;
            float _HighlightPower;

            struct Interpolators
            {
                float4 clipPos:POSITION;
                float3 worldNormal:TEXCOORD1;
                float3 objPos:COLOR;
            };


            Interpolators vert(appdata_base v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.worldNormal=v.normal;
                o.objPos=v.vertex;
                return o;
            }
            float4 frag(Interpolators IN):COLOR
            {
                fixed4 finalColor=UNITY_LIGHTMODEL_AMBIENT;

                float3 normalWorld=UnityObjectToWorldNormal(IN.worldNormal);
                float3 lightDir=normalize(WorldSpaceLightDir(IN.objPos));
                float diffuseFactor=saturate(dot(normalWorld,lightDir));
                finalColor+=_LightColor0*_DiffuseTint*diffuseFactor;

                float3 viewDir=normalize(WorldSpaceViewDir(IN.objPos));
                float3 reflectionDir=2*dot(normalWorld,lightDir)*normalWorld-lightDir;
                float specularFactor=saturate(dot(reflectionDir,viewDir));
                finalColor+=_HighlightColor*pow(specularFactor,_HighlightPower);

                float3 worldPosition=mul(unity_ObjectToWorld,IN.objPos).xyz;
                finalColor.rgb+=Shade4PointLights
                (
                    unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,
                    unity_LightColor[0].rgb,unity_LightColor[1].rgb,unity_LightColor[2].rgb,unity_LightColor[3].rgb,
                    unity_4LightAtten0,
                    worldPosition,normalWorld
                );
                return finalColor;
            }

            ENDCG
        }
    }
    //O fallback para Diffuse permite sombras tanto de luzes direcionais quanto pontuais
    fallback "Diffuse"
}

  1. Efeito de Transparência Baseado em Ângulo de Visão

Este shader produz um efeito de transparência que varia conforme a orientação da superfície em relação à câmera. Áreas que apontam diretamente para o observador ficam opacas, enquanto as bordas se tornam translúcidas.

Subshader
    {
        Tags{"Queue"="Transparent"}
        pass{
            blend srcAlpha oneMinusSrcAlpha
            zwrite off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            float _Intensity;

            struct Interpolators
            {
                float4 clipPos:POSITION;
                float3 surfNormal:TEXCOORD0;
                float3 surfPos:TEXCOORD1;
            };


            Interpolators vert(appdata_base v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.surfNormal=v.normal;
                o.surfPos=v.vertex;
                return o;
            }
            float4 frag(Interpolators IN):COLOR
            {
                float3 n=UnityObjectToWorldNormal(IN.surfNormal);
                float3 v=UnityWorldSpaceViewDir(IN.surfPos);
                float rimFactor=1.0-saturate(dot(n,v));
                rimFactor=pow(rimFactor,_Intensity);
                return fixed4(1,1,1,1)*rimFactor;
            }

            ENDCG
        }
    }

}

  1. Contorno Estilizado com Multiplas Passagens

Esta técnica utiliza três passagens de renderização para criar um contorno visível ao redor do objeto. A primeira passagem expande os vértices ao longo das normais e desenha o contorno, a segunda limpa o buffer alfa, e a terceira renderiza a superfície principal.

Shader "Tutorial/ContornoEstilizado" {
    properties
    {
        _SurfaceColor("Cor da Superfície",color)=(1,1,1,1)
        _EdgeIntensity("Intensidade da Borda",range(1,8))=2
        _ExpandAmount("Expansão do Contorno",range(0,1))=0.2
    }
    Subshader
    {
        Tags{"Queue"="Transparent"}

        //Passagem 1: Desenha o contorno expandido
        pass{
            blend srcAlpha oneMinusSrcAlpha
            zwrite off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            float4 _SurfaceColor;
            float _EdgeIntensity;
            float _ExpandAmount;

            struct Interpolators
            {
                float4 clipPos:POSITION;
                float3 surfNormal:TEXCOORD0;
                float3 surfPos:TEXCOORD1;
            };


            Interpolators vert(appdata_base v)
            {
                v.vertex.xyz+=v.normal*_ExpandAmount;
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.surfNormal=v.normal;
                o.surfPos=v.vertex;
                return o;
            }
            float4 frag(Interpolators IN):COLOR
            {
                float3 n=UnityObjectToWorldNormal(IN.surfNormal);
                float3 v=UnityWorldSpaceViewDir(IN.surfPos);
                float edge=saturate(dot(n,v));
                edge=pow(edge,_EdgeIntensity);
                _SurfaceColor.a*=edge;
                return _SurfaceColor;
            }

            ENDCG
        }
    
        //Passagem 2: Limpa o canal alfa
        pass{
            blendop revsub
            blend dstalpha one
            zwrite off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            struct Interpolators
            {
                float4 clipPos:POSITION;
            };


            Interpolators vert(appdata_base v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                return o;
            }
            float4 frag(Interpolators IN):COLOR
            {
                return fixed4(1,1,1,1);
            }

            ENDCG
        }
    
        //Passagem 3: Renderiza a superfície interna
        pass{
            blend srcAlpha oneMinusSrcAlpha
            zwrite off

            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            float _EdgeIntensity;

            struct Interpolators
            {
                float4 clipPos:POSITION;
                float3 surfNormal:TEXCOORD0;
                float3 surfPos:TEXCOORD1;
            };


            Interpolators vert(appdata_base v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.surfNormal=v.normal;
                o.surfPos=v.vertex;
                return o;
            }
            float4 frag(Interpolators IN):COLOR
            {
                float3 n=UnityObjectToWorldNormal(IN.surfNormal);
                float3 v=UnityWorldSpaceViewDir(IN.surfPos);
                float rim=saturate(dot(n,v));
                rim=pow(rim,_EdgeIntensity);
                return fixed4(1,1,1,1)*rim;
            }

            ENDCG
        }
    }

}

  1. Mapeamento de Textura com Lightmap

Este shader combina uma textura albedo com uma lightmap pré-calculada. As coordenadas UV são transformadas utilizando as variáveis de escala e deslocamento (_ST) para que as propiredades Tiling e Offset do Inspector funcionem corretamente.

Shader "Tutorial/TexturaComLightmap" {
    Properties {
        _AlbedoMap ("Textura Base", 2D) = "white" {}
    }
    SubShader {
        pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            sampler2D _AlbedoMap;
            float4 _AlbedoMap_ST;

            struct Interpolators{
                float4 clipPos:POSITION;
                float2 baseUV:TEXCOORD0;
                float2 lightmapUV:TEXCOORD1;
            };
            Interpolators vert(appdata_full v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.baseUV=TRANSFORM_TEX(v.texcoord,_AlbedoMap);
                o.lightmapUV=v.texcoord1.xy*unity_LightmapST.xy+unity_LightmapST.zw;
                return o;

            }
            fixed4 frag(Interpolators IN):COLOR
            {
                float3 illumination=DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,IN.lightmapUV));
                fixed4 surfaceColor=tex2D(_AlbedoMap,IN.baseUV);
                surfaceColor.rgb*=illumination;
                return surfaceColor;
            }
            ENDCG
        }
    }
}

  1. Efeito de Ondulação na Textura

Este shader aplica distorções nas coordenadas UV para criar efeitos visuais como ondulações de água ou ondas concêntricas. A deformação é animada utilizando o tempo global do shader (_Time.y).

Shader "Tutorial/EfeitoOndulacao" {
    Properties {
        _BaseTexture ("Textura Base", 2D) = "white" {}
        _Frequency("Frequência",range(1,30))=10
        _Amplitude("Amplitude",range(0,0.1))=0.01
        _Radius("Raio",range(0,1))=0
    }
    SubShader {
        pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            sampler2D _BaseTexture;
            float4 _BaseTexture_ST;
            float _Frequency;
            float _Amplitude;
            float _Radius;

            struct Interpolators{
                float4 clipPos:POSITION;
                float2 texUV:TEXCOORD0;
            };
            Interpolators vert(appdata_full v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.texUV=TRANSFORM_TEX(v.texcoord,_BaseTexture);
                return o;

            }
            fixed4 frag(Interpolators IN):COLOR
            {
                float2 currentUV=IN.texUV;
                float distFromCenter=distance(currentUV,float2(0.5,0.5));
                float waveOffset=0;
                float adjustedAmp=_Amplitude*saturate(1-distFromCenter/_Radius);
                waveOffset=adjustedAmp*sin(-distFromCenter*3.14*_Frequency+_Time.y);
                currentUV=currentUV+currentUV*waveOffset;
                fixed4 result=tex2D(_BaseTexture,currentUV);
                return result;
            }
            ENDCG
        }
    }
}

  1. Desfoque por Distância (Mipmap Controlado por Derivadas)

Este shader utiliza derivadas parciais (ddx e ddy) para controlar o nível de mipmap aplicado à textura. Superfícies voltadas diretamente para a câmera aparecem nítidas, enquanto superfícies anguladas ou distentes recebem um desfoque natural.

Shader "Tutorial/DesfoqueDistancia" {
    Properties {
        _BaseTexture ("Textura Base", 2D) = "white" {}
    }
    SubShader {
        pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
            #pragma target 3.0
            sampler2D _BaseTexture;
            float4 _BaseTexture_ST;

            struct Interpolators{
                float4 clipPos:POSITION;
                float2 texUV:TEXCOORD0;
                float depthZ:TEXCOORD1;
            };
            Interpolators vert(appdata_full v)
            {
                Interpolators o;
                o.clipPos=UnityObjectToClipPos(v.vertex);
                o.texUV=TRANSFORM_TEX(v.texcoord,_BaseTexture);
                o.depthZ=mul(unity_ObjectToWorld,v.vertex).z;
                return o;

            }
            fixed4 frag(Interpolators IN):COLOR
            {
                float2 gradX=ddx(IN.depthZ)*10;
                float2 gradY=ddy(IN.depthZ)*10;
                fixed4 sampledColor=tex2D(_BaseTexture,IN.texUV,gradX,gradY);
                return sampledColor;
            }
            ENDCG
        }
    }
}

Tags: Unity ShaderLab CG/HLSL Fragment Shader Lighting Model

Publicado em 7-19 22:05