Multi Texture and Multi UV Normal-Diffuse

Hello everybody,

For the project I’m working on, I need a shader capable of combining two diffuse textures and apply a normal map on it for the lightning.

I’m new to shader creation using CG, but I managed to make something work, approximately.

Here is my code :

Shader "@My Normal Multi" {
Properties {
	_Color ("Main Color", Color) = (1,1,1,1)
	_MainTex ("Base (RGB)", 2D) = "white" {}
	_SecondaryTex ("Second (RGB) Mask(A)", 2D) = "white" {}
	_BumpMap ("Bumpmap (RGB)", 2D) = "bump" {}
	_TestColor ("Test Color" , Color) = (0.5,0.5,0.5,1)
	_NormalIntensity ("Normal intensity", Range(0,1)) = 0.5
	_BlendFloat ("Blending", Range(0,1)) = 0.5
	_NormalMask ("Normal Mask", Range(0,1)) = 0.5
}

Category {
	Tags { "RenderType"="Opaque" }
	LOD 300
	Blend AppSrcAdd AppDstAdd
	Fog { Color [_AddFog] }

	// ------------------------------------------------------------------
	// ARB fragment program
	
	SubShader {
		
		// Pixel lights
		Pass {	
			Name "PPL" 
			Tags { "LightMode" = "Pixel" }
				
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_builtin
			#pragma fragmentoption ARB_fog_exp2
			#pragma fragmentoption ARB_precision_hint_fastest
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"

			struct v2f {
				V2F_POS_FOG;
				LIGHTING_COORDS
				float2	uv;
				float2	uv2;
				float2	uvBump;
				
				float3	lightDirT;
				float3	viewDirT;
				
				float3	Normal;
				float4	NormalColor : COLOR;
			};
			
			uniform float4 _MainTex_ST, _BumpMap_ST, _SecondaryTex_ST;
			
			//get vertex data : texture coordinates 01, position, normal, tangent
			struct appdata {
				float4 texcoord;
				float4 texcoord1;
				float4 vertex;
				float3 normal;
				float4 tangent;
			};
			
			uniform float4 _MainColor;
			uniform float4 _TestColor;
			uniform float _NormalIntensity;
			uniform float _BlendFloat;
			uniform float _NormalMask;			
			
			uniform sampler2D _MainTex, _SecondaryTex, _BumpMap;

			v2f vert (appdata v)
			{
				v2f o;
				PositionFog( v.vertex, o.pos, o.fog );
				o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
				o.uv2 = TRANSFORM_TEX(v.texcoord1,_SecondaryTex);
				o.uvBump = TRANSFORM_TEX(v.texcoord,_BumpMap);
				
				o.Normal = v.normal;
				o.NormalColor = float4(v.tangent.xyz*0.5+0.5, 1);
				
				TANGENT_SPACE_ROTATION;
				o.lightDirT = mul( rotation, ObjSpaceLightDir( v.vertex ) );
				o.viewDirT = mul( rotation, ObjSpaceViewDir( v.vertex ) );	
				
				TRANSFER_VERTEX_TO_FRAGMENT(o);
				return o;
			}


			//Light using the normal map
			float4 frag (v2f i) : COLOR
			{				
				float4 MainColor = tex2D(_MainTex,i.uv);
				float4 SecondColor = tex2D(_SecondaryTex,i.uv2);
				float4 NormalMap = tex2D(_BumpMap, i.uvBump);
				
				float coef = SecondColor.a*_BlendFloat;
				
				float4 texcol = lerp (MainColor, SecondColor, coef);
				
				//mask the normal map with a (0.0.1) normal (local to uv) using the vertex normal color
				float4 NormalColor = lerp (NormalMap, float4(0.5,0.5,1,1), coef*_NormalMask);
				//Attenuate the normal 
				NormalColor = lerp (NormalColor, float4(0.5,0.5,1,1),1-_NormalIntensity);
				
				// get normal from the normal color
				float3 normal = NormalColor.xyz * 2 - 1;
				
				half4 Light = DiffuseLight( i.lightDirT, normal, texcol, LIGHT_ATTENUATION(i) );
				
				return Light;			
			}
			ENDCG  
		}
	}
}

FallBack "Diffuse", 1

}

It seams to work well, but I have a few problemw with it.

First, with this part :

o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
				o.uv2 = TRANSFORM_TEX(v.texcoord1,_SecondaryTex);
				o.uvBump = TRANSFORM_TEX(v.texcoord,_BumpMap);
				
				o.Normal = v.normal;
				o.NormalColor = float4(v.tangent.xyz*0.5+0.5, 1);

I first wanted to use the vertex local normals in order to attenuate the original normal map (so we can have a little tweak for the normal intensity), but it seems that when using texcoord AND texcoord1, the vertex normals are getting messed up (I saw they tried to match something like : normal = uv2).
Is there a proper way to attenuate the normals ?

Second :
My actual shader is affected by only one light, does somebody know why ?

I don’t know if I’m using the best actual method to do what I want, so, if anybody has an answer for my questions, or a better way to do all this stuff, I’m open for any suggestions :).

second: quality settings in the player potentially / likely

Well, it could be a reason, but in my case, it isn’t that. When I said that my shader is only affected by one light, it’s because it’s what I see in the viewport. And the standard normal-bumped shader is affected by more than one light, always in my viewport.
It seems that since the moment I used a second texture in the fragment shader, the other lights went off.

It seams that as soon as I use texcoord and texcoord1 the vertex fragment shader is messed up.
Has someone an example of good working cg shader using 2 textures coordinates ?

Also, I looked at the standard normal bumped shader and the normal bumped specular, and I don’t understant why there is an ambiant and diffuse pass before the CGPROGRAM, doesn’t the CGPROGRAM overwright everything at rendering ?

If I’m wrong, how could I mix my texture using standard shader lab and light with the normal map using CGPROGRAM ?

An other thing is that when exporting a mesh from softimage with mutliples UV and only 1 material everything seems to work fine, but when using more than 1 material (on one single polymesh) my uv’s are getting shifted.
Looking at the fbx in ascii format, with 1 material I have :
LayerElementUV0 = uv0
LayerElementUV1 = uv1

But with 2 materials :
LayerElementUV0 = uv0
LayerElementUV1 = uv0
LayerElementUV2 = uv1

With this configuration my shader isn’t working anymore, I have to split my polymesh in multiples polymeshes using 1 material each, but this causes non smoothed normal on my object.

Rhaaa, sometime Unity is such a pain -_- .

About your texcoord/texcoord1 problem:
I had the same all-messed-up experience the other day and got rid of it by using texcoord1 and texcoord2 instead of texcoord and texcoord1. Maybe that helps. :slight_smile:

just wanted to say thnx, i’ve been working on my first shader ever… pffff :stuck_out_tongue:

anyway, I needed to blend two textures, none of the normal/bump stuff, just full diffuse, shadows, etc.

will post it after testing it some more :wink:

Were did you changed this, in the shaderlab “BindChannels” part ?
I was talking about my “struct appdata”.
If I use texcoord2 in the CGPROGRAM, it seems to not understand it.
In the documentation here -http://unity3d.com/support/documentation/Components/SL-VertexProgramInputs.html-it is only said that texcoord and texcoord1 can be passed in the CGPROGRAM.

But I found that instead of using 2 float2 for the second uv and the bump uv, if I use one float4 containing both uvs, it works fine.
Can somebody explain me why it works in this case ???

Actually, my shader is almost working, but I reached the 64 arithmetical operations for sm2 shader, an when trying to force in sm3, the shader compile, but display like a simple diffuse … but my computer has directX9 and a sm3 compatible card…

I know it’s an old post, but here goes!:

Could you show us how did your vertex appdata struct looks like? How did you merge both float2 uv texcoords into a single float4?

Some time ago I found a post that stated that you could do things like float4 worldNormalLightPosx in surface shaders, and that would contain the normals in .xyz and .w would contain the light’s position .X component (useful when you are running out of TEXCOORD interpolators). But it never worked for me and really I didn’t give it more than a few minutes.

Also: did you manage to crunch it down back to 64 ALU ops?
(Hope this helps a little bit!)

  • I don’t know how unity handles shader compiling and optimizations (and if there are preshaders and whatnot), but if you are only slightly off the 64 limit you could win a little bit by avoiding things like 1 - _NormalIntensity and name it like _InvNormalIntensity expecting it to be inverted already.

  • I don’t know the pure math around it, but I guess that doing several linear interpolations on the same numbers could be done in a single one, it all goes down to calculating the lerp factor right (a single float that could be calculated with standard math ops against two lerps of float4’s). Although lerp is a fairly cheap method, and the GPU’s doesn’t really cares if you give them a float or a float4 to process…

  • Normals being normalized vectors, perhaps if you work with them unpacked from the beginning of the shader (i.e. normal * 2.0f - 1.0f) you could apply tricks, I really wouldn’t know, but perhaps work with xy and the sign of z (normal maps being tanget space, should always have a positive Z?), think about dot products (super-cheap for the gpu), and normalize at the end. (Always normalize, i.e. after lerping the normalMap and normaColors the final vector has already lost its length of 1.