Shader programming question for Cg expert : Scaling textures

Hi folks,

This is a follow up to my question here:

I have been learning a little about shader programming since I asked that question, and now I’m wondering if its possible to solve this in Cg with a custom particle shader. It feels like it is, but I am having a hard time finding examples.

What I would like to do, is scale my texture vertically by negative 1 when my particle is rotated between 90º and 270º.
It seems like I could do the scaling by muliplying my texture transformation matrix: (UNITY_MATRIX_TEXTURE0) by this matrix: ((1,0,0,0),(0,-1,0,0),(0,0,1,0),(0,0,0,1)). I would need to do this, only if my particle is rotated which I believe I can somehow ascertain from the projection transform matrix (UNITY_MATRIX_MVP).

Thats about as far as I got before I hit a wall though. I can’t find many examples out there, and my grasp of Cg and matrix math is very shaky. Does someone more knowledgable know if A: this is possible. B: can you give me some code samples or pointers.

Thanks much,

-Matt

It’s not possible, because the MVP is for an entire object (the particle system in this case). The mesh data for the system is inaccessible to us, so you need to write your own particle system if some number of Unity’s can’t get you what you need.

The comments are getting way too long, but this is a reply to the OPs giant comment.

The shader you’ve got uses the old “switches” method, which Unity calls shaderLab. You can set lots of values, such as “blend with the previous texture, based on one minus its alpha”, but can’t actually write code. For anything odd (“blue flips texture” counts as odd) you have to get the “write your code here” version.

I assume you started with something dragged in from builtin_shaders (Didn’t see your’s there. Is it mobile? If you got it some where else builtin_shaders is downloadable from Unity.) If you drag in something like Particle/AlphaBlend shader, you’ll see spots to write code:

v2f vert (appdata_t v) {
  v2f o;
  o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
      [.. deleted soft particles ..]
  o.color = v.color;
  o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
  return o;
}

This runs for each corner of the billboard. Since you’re using the color as a marker, and not for color, don’t copy it – just set to white. This should be self-explainitory:

      [.. deleted soft particles ..]
  o.color = 1;    // sets tintcolor to opaque white ("do nothing")
  o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
  //NOTE: the blue value is either 0 (if we are red) or 1 (if we are blue)
  if(v.color.b>0.5) o.texcoord.y*=-1;
  return o;

There are some improvements. For example the pixel shader (just below – “frag”) multiples everything by o.color (it calls it i.color.) You could just delete that part and not set o.color. TRANSFORM_TEX applies the tiling and offset. You probably will never have any, for fish, so could delete that.

Ok, the texture flipping is working great. No more upside down fish! I’m now trying to get my vertex lighting, and emissive texture back into my particles. I’ve tried turning lighting tags on, but that seems to have no effect. I tried getting the vertex color, but that doesn’t seem to incorporate the lighting like “primary” did in my old shader. I tried making a separate pass using the previous syntax, but that seems to draw one over the other without combining. I tried enabling #pragma surface surf Lambert but the particle renderer doesn’t seem to like that. Here is my current code. Any tips for adding vertex lighting, and incorporating my emissive texture?

Thanks for both of your help.

-Matt

Shader “FishParticles” {
Properties {
_TintColor (“Tint Color”, Color) = (0.5,0.5,0.5,0.5)
_MainTex (“Particle Texture”, 2D) = “white” {}
_EmisTex (“Emmision Texture”, 2D) = “white” {}
}

Category {
Tags { “Queue”=“Transparent” “IgnoreProjector”=“True” “RenderType”=“Transparent” }
Blend SrcAlpha OneMinusSrcAlpha
AlphaTest Greater .01
ColorMask RGB
Cull Off Lighting On ZWrite On Fog { Color (0,0,0,0) }
BindChannels {
Bind “Color”, color
Bind “Vertex”, vertex
Bind “TexCoord”, texcoord
}

SubShader {

	Pass {
	
		CGPROGRAM
		#pragma vertex vert
			//		#pragma surface surf Lambert
		#pragma fragment frag
		#pragma fragmentoption ARB_precision_hint_fastest
		#pragma multi_compile_particles
		
		#include "UnityCG.cginc"

		sampler2D _MainTex;
		sampler2D _EmisTex;
		fixed4 _TintColor;
		
		struct appdata_t {
			float4 vertex : POSITION;
			fixed4 color : COLOR;
			float2 texcoord : TEXCOORD0;
			float2 texcoord1 : TEXCOORD1;
		};

		struct v2f {
			float4 vertex : POSITION;
			fixed4 color : COLOR;
			float2 texcoord : TEXCOORD0;
			float2 texcoord1 : TEXCOORD1;
		};
		
		float4 _MainTex_ST;

		v2f vert (appdata_t v)
		{
			v2f o;
			o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
			o.color = 1;//v.color;
			o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
			o.texcoord1 = TRANSFORM_TEX(v.texcoord,_MainTex);
			if(v.color.b>0.5) o.texcoord.y*=-1;
			if(v.color.b>0.5) o.texcoord1.y*=-1;
			return o;
		}
		
		fixed4 frag (v2f i) : COLOR
		{
			
			return i.color* tex2D(_MainTex, i.texcoord);
		}
		ENDCG 
		
	}
	/*Pass { 

	SetTexture [_MainTex] { combine primary * texture }
	SetTexture [_EmisTex] { combine previous + texture }
}*/
} 	

}
}

The following shader is the result of Owen’s help with shader code, and Jesse’s clever idea of using vertex color information to tell the particles to right themselves. It solves my original question, so I’m going to mark this answered. Unfortunately, I also needed the particles to be vertex lit, and we haven’t been able to get that to work with the flipped texture. This one, the particles will take their color from the primary light, but thats as close as I’ve been able to come to get the lighting working.

Shader "FishParticles4" {
Properties {
	_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
	_MainTex ("Particle Texture", 2D) = "white" {}
	_EmisTex ("Emmision Texture", 2D) = "white" {}
}

CGINCLUDE

#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"


uniform sampler2D _MainTex;
uniform sampler2D _EmisTex;

ENDCG

SubShader
{
    Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "LightMode" = "ForwardBase"}
    
    Blend SrcAlpha OneMinusSrcAlpha
	AlphaTest Greater .01
	ColorMask RGB
	Cull Off Lighting On ZWrite On

    Pass
    {
       Lighting On


       CGPROGRAM 

       #pragma vertex vert
       #pragma fragment frag
       #pragma multi_compile_fwdbase
       #pragma multi_compile_particles

		struct appdata_t {
				float4 vertex : POSITION;
				fixed4 color : COLOR;
				float2 texcoord : TEXCOORD0;
				float3 normal : NORMAL;
			};

       struct VSOut
       {
         float4 pos     : SV_POSITION;
         fixed4 color : COLOR;
         float2 uv       : TEXCOORD1;
         LIGHTING_COORDS(3,4)
       };

       VSOut vert(appdata_t v)
       {
         VSOut o;
         o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
         o.uv = v.texcoord.xy;
         if(v.color.b>0.5) o.uv.y*=-1;
         
         float3 ldir = normalize( ObjSpaceLightDir( v.vertex ) );
		 float diffuse = dot( v.normal, ldir );
         
         o.color = 1 * unity_LightColor[0];//1;

         TRANSFER_VERTEX_TO_FRAGMENT(o);

         return o;
       }

       float4 frag(VSOut i) : COLOR
       { 
         return i.color*tex2D(_MainTex, i.uv)+tex2D(_EmisTex, i.uv);
       }

       ENDCG
    }
} 
FallBack "Diffuse"
}

Use this shader with the following javascript to change the particle color:

function LateUpdate () {

var particles = particleEmitter.particles;

for (var i = 0; i < particles.Length; i++) {
    var m : Matrix4x4 = Camera.main.worldToCameraMatrix;
    var v : Vector3 = m.MultiplyVector(particles*.velocity);*

if (v.x<=0){particles_.color = Color.red;} else{particles*.color = Color.blue;}*_

particleEmitter.particles = particles;
}

To just flip upside down, put o.texcoord.y*=-1; in the vertex shader (just after the line that computes o.texcoord.) To verify this, try using *=-2. That will double the vertical fish, as well as flipping them.

Turns out the texture matrix isn’t used that much – if you rotate the model (in this case, the plane billboard,) the tex-coords go with it anyway. The texMatrix is for when you have a stationary billboard and want to spin the texture (which is rare.)

But, a quick test with the MV and MVP matrices has me baffled. We really want the MV matrix – model rotation with respect to the camera. The P stands for projection, and is just there to adjust size for depth. The shader can’t “see” the motion, but the billboard is obviously rotated, with a “wrong way” rotated 180 degrees.

The thing is, testing shows the rotation numbers in MV and MVP don’t change in any useful way (is there a Unity shader debugger?) Rotation about just z, for example, should change slots 00, 02, 20 and 22. The test (in the vert shader):

if(UNITY_MATRIX_MVP._m00>0.05) // && UNITY_MATRIX_MVP._m00<1.01)
  o.texcoord.y=0.5; // in-your-face test value

This changes as you scroll in and out (for depth,) but doesn’t seem to care about rotated particles. If you leave out the P, 00 is always 1 (the identity.) It appears as if the secret particle code is adjusting the billboard verts itself, and the shader has no information about the spin.

Adding Update code to the emitter can set shader values for everyone, but I don’t think for each fish. Aren’t there fish that look about the same upside down?