@Quiensabe
VR-разработчик

Как умножить текстуры в шейдере Unity?

В Unity3D есть шейдер проектора, позволяющий проецировать текстуру на объекты сцены. Я пытаюсь добавить в этот шейдер возможность наложить маску на проецируемое изображение, но опыта в создании шейдеров не хватает.

В обычном шейдере объекта можно легко объединять текстуры, но в шейдере проектора - используется tex2Dproj, и я никак не могу с ним разобраться. На мой взгляд проще всего, заранее смикшировать две текстуры (изображение и маску), и поместить их в отдельную текстуру, которую потом накладывать на объект. Но не могу понять, как это сделать? Везде рассматривается вариант, когда две текстуры смешиваются и сразу выводятся на объект, а мне нужно временно помещать результат в третью текстуру, и только потом выводить...

Конечно, это можно сделать скриптом, но хочется задействовать шейдер, чтобы можно было развивать подход, например, добавив динамические/вращающиеся маски и т.п.

Ниже цепляю код стандартного шейдера проектора. Буду благодарен за любые советы. Спасибо!
Shader Projector/Multiply
Shader "Projector/Multiply" {
	Properties {
		_ShadowTex ("Cookie", 2D) = "gray" {}
		_FalloffTex ("FallOff", 2D) = "white" {}
	}
	Subshader {
		Tags {"Queue"="Transparent"}
		Pass {
			ZWrite Off
			ColorMask RGB
			Blend DstColor Zero
			Offset -1, -1

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fog
			#include "UnityCG.cginc"
			
			struct v2f {
				float4 uvShadow : TEXCOORD0;
				float4 uvFalloff : TEXCOORD1;
				UNITY_FOG_COORDS(2)
				float4 pos : SV_POSITION;
			};
			
			float4x4 unity_Projector;
			float4x4 unity_ProjectorClip;
			
			v2f vert (float4 vertex : POSITION)
			{
				v2f o;
				o.pos = UnityObjectToClipPos (vertex);
				o.uvShadow = mul (unity_Projector, vertex);
				o.uvFalloff = mul (unity_ProjectorClip, vertex);
				UNITY_TRANSFER_FOG(o,o.pos);
				return o;
			}
			
			sampler2D _ShadowTex;
			sampler2D _FalloffTex;
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
				texS.a = 1.0-texS.a;

				fixed4 texF = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff));
				fixed4 res = lerp(fixed4(1,1,1,0), texS, texF.a);

				UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1));
				return res;
			}
			ENDCG
		}
	}
}
  • Вопрос задан
  • 766 просмотров
Решения вопроса 1
BasmanovDaniil
@BasmanovDaniil
Геймдизайнер-телепат
Шейдеры для прожекторов работают точно так же, просто uv-карта рассчитывается по-другому:
Projector/Multiply
Shader "Projector/Multiply"
{
    Properties
    {
        _ShadowTex("Cookie", 2D) = "gray" {}
        _FalloffTex("FallOff", 2D) = "white" {}
        // Добавляем свойство
        _MaskTex("Mask", 2D) = "white" {}
    }
    Subshader
    {
        Tags {"Queue"="Transparent"}
        Pass
        {
            ZWrite Off
            ColorMask RGB
            Blend DstColor Zero
            Offset -1, -1

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            #include "UnityCG.cginc"
            
            struct v2f
            {
                float4 uvShadow : TEXCOORD0;
                float4 uvFalloff : TEXCOORD1;
                UNITY_FOG_COORDS(2)
                float4 pos : SV_POSITION;
            };
            
            float4x4 unity_Projector;
            float4x4 unity_ProjectorClip;
            
            v2f vert (float4 vertex : POSITION)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.uvShadow = mul(unity_Projector, vertex);
                o.uvFalloff = mul(unity_ProjectorClip, vertex);
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }
            
            sampler2D _ShadowTex;
            sampler2D _FalloffTex;
            // Добавляем переменную, чтобы использовать текстуру внутри прохода
            sampler2D _MaskTex;
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 texS = tex2Dproj(_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
                texS.a = 1.0 - texS.a;

                fixed4 texF = tex2Dproj(_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff));
                fixed4 res = lerp(fixed4(1, 1, 1, 0), texS, texF.a);

                // Сэмплируем текстуру
                fixed4 mask = tex2Dproj(_MaskTex, UNITY_PROJ_COORD(i.uvShadow));
                // Применяем маску
                res *= mask;

                UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1, 1, 1, 1));
                return res;
            }
            ENDCG
        }
    }
}

Чтобы лучше понять что из себя представляют uvShadow и uvFalloff, можете вывести их на экран во фрагментном шейдере:
fixed4 frag (v2f i) : SV_Target
{
    float2 uvShadow = UNITY_PROJ_COORD(i.uvShadow);
    float2 uvFalloff = UNITY_PROJ_COORD(i.uvFalloff);
    return fixed4(uvShadow, 0.0, 1.0);
    //return fixed4(uvFalloff, 0.0, 1.0);
}
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы