CG/Unity: Toy Shader For Duplicating Object - unity3d

I'm new to writing shaders and I'm working on a practice geometry shader. The goal of the shader is to make the "normal" pass produce transparent pixels such that the object is invisible, while the "geometry" pass will take each triangle, redraw in the same place as the original but colored black. Thus, I expect the output to be the original object, but black. However, my geometry pass seems to not produce any output I can see:
Here is the code I currently have for the shader.
Shader "Outlined/Silhouette2" {
Properties
{
_Color("Color", Color) = (0,0,0,1)
_MainColor("Main Color", Color) = (1,1,1,1)
_Thickness("Thickness", float) = 4
_MainTex("Main Texture", 2D) = "white" {}
}
SubShader
{
Tags{ "Queue" = "Geometry" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Back
ZTest always
Pass
{
Stencil{
Ref 1
Comp always
Pass replace
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct v2g
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewT : TANGENT;
float3 normals : NORMAL;
};
struct g2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewT : TANGENT;
float3 normals : NORMAL;
};
float4 _LightColor0;
sampler2D _MainTex;
float4 _MainColor;
v2g vert(appdata_base v)
{
v2g OUT;
OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
OUT.uv = v.texcoord;
OUT.normals = v.normal;
OUT.viewT = ObjSpaceViewDir(v.vertex);
return OUT;
}
half4 frag(g2f IN) : COLOR
{
//this renders nothing, if you want the base mesh and color
//fill this in with a standard fragment shader calculation
float4 texColor = tex2D(_MainTex, IN.uv);
float3 normal = mul(float4(IN.normals, 0.0), _Object2World).xyz;
float3 normalDirection = normalize(normal);
float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz * -1);
float3 diffuse = _LightColor0.rgb * _MainColor.rgb * max(0.0, dot(normalDirection, lightDirection));
texColor = float4(diffuse,1) * texColor;
//
//return texColor;
return float4(0, 0, 0, 0);
}
ENDCG
}
Pass
{
Stencil{
Ref 0
Comp equal
}
CGPROGRAM
#include "UnityCG.cginc"
#pragma target 4.0
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
half4 _Color;
float _Thickness;
struct v2g
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float4 local_pos: TEXCOORD1;
float3 viewT : TANGENT;
float3 normals : NORMAL;
};
struct g2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewT : TANGENT;
float3 normals : NORMAL;
};
v2g vert(appdata_base v)
{
v2g OUT;
OUT.pos = mul(UNITY_MATRIX_MVP, v.vertex);
OUT.local_pos = v.vertex;
OUT.uv = v.texcoord;
OUT.normals = v.normal;
OUT.viewT = ObjSpaceViewDir(v.vertex);
return OUT;
}
[maxvertexcount(12)]
void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
{
g2f OUT;
OUT.pos = IN[0].pos;
OUT.uv = IN[0].uv;
OUT.viewT = IN[0].viewT;
OUT.normals = IN[0].normals;
triStream.Append(OUT);
OUT.pos = IN[1].pos;
OUT.uv = IN[1].uv;
OUT.viewT = IN[1].viewT;
OUT.normals = IN[1].normals;
triStream.Append(OUT);
OUT.pos = IN[2].pos;
OUT.uv = IN[2].uv;
OUT.viewT = IN[2].viewT;
OUT.normals = IN[2].normals;
triStream.Append(OUT);
}
half4 frag(g2f IN) : COLOR
{
_Color.a = 1;
return _Color;
}
ENDCG
}
}
FallBack "Diffuse"
}
Since all I'm doing is taking the same triangles I've been given and appending them to the triangle stream I'm not sure what I could be doing wrong to cause nothing to appear. Anyone know why this is happening?

I notice that you don't call
triStrem.RestartStrip(); after feeding in 3 vertices of a triangle in your geometry shader.
This informs the stream that a particular triangle strip has ended, and a new triangle strip will begin. If you don't do this, each (single) vertex passed to the stream will add on to the existing triangle strip, using the triangle strip pattern: https://en.wikipedia.org/wiki/Triangle_strip
I'm fairly new to geo-shaders myself, so I'm not sure if this is your issue or not, I don't THINK the RestartStrip function is called automatically at the end of each geomerty-shader, but have not tested this. Rather I think it gets called automatically only when you you reach the maxvertexcount. For a single triangle, I would set the maxvertexcount to 3, not the 12 you have now.
(I also know it can be tough to get ANY shader answers so figgured I'd try to help.)

Related

How to add lightning to a sprite billboard shader?

I'm currently working on large terrains in unity.
I need to use custom trees, that basically hold a 3d model and a sprite. With LOD component the model is disabled and sprite enabled with distance.
I founded a billboard shader on github that's match almost perfectly what's I searched for,
exepting that it isn't using lightning, so the sprite have always the same "Color" with and without light in the scene.
I'm currently trying to mix it with diffuse shader but I don't have any knowledges in shader coding.
Here is the shader in question:
Shader "Custom/BillboardSprite" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _VerticalBillboarding;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v) {
v2f o;
// Suppose the center in object space is fixed
float3 center = float3(0, 0, 0);
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));
float3 normalDir = viewer - center;
// If _VerticalBillboarding equals 1, we use the desired view dir as the normal dir
// Which means the normal dir is fixed
// Or if _VerticalBillboarding equals 0, the y of normal is 0
// Which means the up dir is fixed
normalDir.y =normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
// Get the approximate up dir
// If normal dir is already towards up, then the up dir is towards front
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
float3 rightDir = normalize(cross(upDir, normalDir));
upDir = normalize(cross(normalDir, rightDir));
// Use the three vectors to rotate the quad
float3 centerOffs = v.vertex.xyz - center;
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
o.pos = UnityObjectToClipPos(float4(localPos, 1));
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}

Unity shader - How to generate normal map based on texture map

I'm currently stuck in a shader that i'm writing.
I'm trying to create a rain shader.
I have set up 3 particle system which simulates rain, and a
camera to look at this simulation. The camera view is what I
use as texture. In my shader I am now trying to make a normal map
from that texture map, but I don't know how to do it.
Shader "Unlit/Rain"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
_NormalIntensity("NormalIntensity",Float) = 1
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct VertexInput {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 normal : NORMAL;
float3 tangent : TANGENT;
};
struct VertexOutput {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float2 uv1 : TEXCOORD1;
float4 normals : NORMAL;
float3 tangentSpaceLight: TANGENT;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
float _NormalIntensity;
VertexOutput vert(VertexInput v) {
VertexOutput o;
o.normals = v.normal;
o.uv1 = v.uv;
o.vertex = UnityObjectToClipPos( v.vertex );
// o.uv = TRANSFORM_TEX( v.uv, _MainTex ); // used for texture
return o;
}
float4 frag(VertexOutput i) : COLOR{
float4 col2 = tex2D(_MainTex, i.uv1);
return col2 * i.normals * 5;
}
ENDCG
}
}
}
This is what the camera sees. I set the TargetTexture for this camera to be a texture I created.
In my shader I then put that texture as an albedo property
So what I wanna do is now find the normal for that texture to create a bumpmap.
It looks like your "TargetTexture" is giving you back a height map. Here is a post I found about how to turn a height map into a normal map. I've mashed the code you had originally together with the core of that forum post and output the normals as color so you can test and see how this works:
Shader "Unlit/HeightToNormal"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
_NormalIntensity("NormalIntensity",Float) = 1
_HeightMapSizeX("HeightMapSizeX",Float) = 1024
_HeightMapSizeY("HeightMapSizeY",Float) = 1024
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct VertexInput {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 normal : NORMAL;
float3 tangent : TANGENT;
};
struct VertexOutput {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float2 uv1 : TEXCOORD1;
float4 normals : NORMAL;
//float3 tangentSpaceLight: TANGENT;
};
sampler2D _MainTex;
float4 _MainTex_ST;
half4 _Color;
float _NormalIntensity;
float _HeightMapSizeX;
float _HeightMapSizeY;
VertexOutput vert(VertexInput v) {
VertexOutput o;
o.uv = TRANSFORM_TEX( v.uv, _MainTex ); // used for texture
o.uv1 = v.uv;
o.normals = v.normal;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
float4 frag(VertexOutput i) : COLOR
{
float me = tex2D(_MainTex,i.uv1).x;
float n = tex2D(_MainTex,float2(i.uv1.x, i.uv1.y + 1.0 / _HeightMapSizeY)).x;
float s = tex2D(_MainTex,float2(i.uv1.x, i.uv1.y - 1.0 / _HeightMapSizeY)).x;
float e = tex2D(_MainTex,float2(i.uv1.x + 1.0 / _HeightMapSizeX,i.uv1.y)).x;
float w = tex2D(_MainTex,float2(i.uv1.x - 1.0 / _HeightMapSizeX,i.uv1.y)).x;
// defining starting normal as color has some interesting effects, generally makes this more flexible
float3 norm = _Color;
float3 temp = norm; //a temporary vector that is not parallel to norm
if (norm.x == 1)
temp.y += 0.5;
else
temp.x += 0.5;
//form a basis with norm being one of the axes:
float3 perp1 = normalize(cross(i.normals,temp));
float3 perp2 = normalize(cross(i.normals,perp1));
//use the basis to move the normal i its own space by the offset
float3 normalOffset = -_NormalIntensity * (((n - me) - (s - me)) * perp1 + ((e - me) - (w - me)) * perp2);
norm += normalOffset;
norm = normalize(norm);
// it's also interesting to output temp, perp1, and perp1, or combinations of the float samples.
return float4(norm, 1);
}
ENDCG
}
}
}
To generate a normal map from a height map, you're trying to use the oriented rate of change in your height map to come up with a normal vector which can be represented using 3 float values (or color channels, if it's an image). You can sample the point of image you are on, and then small steps away from that point in four cardinal directions. Using the cross product to guarantee orthogonality you can define a basis. Using your oriented steps on your image, you can scale the two basis vectors and add them together to find a "normal offset", which is the 3D representation approximating the oriented change in value on your heightmap. Basically it's your normal.
You can see the effects of me playing with normal intensity here, and the "normal color" here. When this looks right for your use case, you can try using normals as normals instead of colored output.
Some tweaking of values will probably still be required. Good luck!

Rendering Only point cloud data inside a box

I am trying to render only point cloud data inside a 3d box with a shader.
However, a point cloud data shader uses geometry and a clip box shader uses surface, so I do not know how to combine these two together.
Point Cloud Data Shader
https://answers.unity.com/questions/1437520/implementing-a-geometry-shader-for-a-pointcloud.html
///////////////////////////////////////////
Shader "Custom/Pointcloud" {
Properties{
_Radius("Sphere Radius", float) = 1.0
}
SubShader{
LOD 200
Tags { "RenderType" = "Opaque" }
//if you want transparency
//Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
//Blend SrcAlpha OneMinusSrcAlpha
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma geometry geom
#pragma target 4.0 // Use shader model 3.0 target, to get nicer looking lighting
#include "UnityCG.cginc"
struct vertexIn {
float4 pos : POSITION;
float4 color : COLOR;
};
struct vertexOut {
float4 pos : SV_POSITION;
float4 color : COLOR0;
float3 normal : NORMAL;
float r : TEXCOORD0; // not sure if this is good to do lol
};
struct geomOut {
float4 pos : POSITION;
float4 color : COLO0R;
float3 normal : NORMAL;
};
float rand(float3 p) {
return frac(sin(dot(p.xyz, float3(12.9898, 78.233, 45.5432))) * 43758.5453);
}
float2x2 rotate2d(float a) {
float s = sin(a);
float c = cos(a);
return float2x2(c,-s,s,c);
}
//Vertex shader: computes normal wrt camera
vertexOut vert(vertexIn i) {
vertexOut o;
o.pos = UnityObjectToClipPos(i.pos);
o.color = i.color;
o.normal = ObjSpaceViewDir(o.pos);
o.r = rand(i.pos);// calc random value based on object space pos
// from world space instead (particles will spin when mesh moves, kinda funny lol)
//o.r = rand(mul(unity_ObjectToWorld,i.pos));
return o;
}
float _Radius;
//Geometry shaders: Creates an equilateral triangle with the original vertex in the orthocenter
[maxvertexcount(3)]
void geom(point vertexOut IN[1], inout TriangleStream<geomOut> OutputStream)
{
float2 dim = float2(_Radius,_Radius);
float2 p[3]; // equilateral tri
p[0] = float2(-dim.x, dim.y * .57735026919);
p[1] = float2(0., -dim.y * 1.15470053838);
p[2] = float2(dim.x, dim.y * .57735026919);
float2x2 r = rotate2d(IN[0].r * 3.14159);
geomOut OUT;
// OUT.color = IN[0].color;
OUT.color = IN[0].color;
OUT.normal = IN[0].normal;
for (int i = 0; i < 3; i++) {
p[i] = mul(r,p[i]); // apply rotation
p[i].x *= _ScreenParams.y / _ScreenParams.x; // make square
OUT.pos = IN[0].pos + float4(p[i],0,0) / 2.;
OutputStream.Append(OUT);
}
}
float4 frag(geomOut i) : COLOR
{
return i.color;
// could do some additional lighting calculation here based on normal
}
ENDCG
}
}
FallBack "Diffuse"
}
ClibBox shader
https://answers.unity.com/questions/1762908/render-only-whats-inside-a-box.html
Shader "Custom/ClipBox" {
Properties{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
}
SubShader{
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows addshadow
#pragma target 3.0
sampler2D _MainTex;
half _Glossiness;
half _Metallic;
float4x4 _WorldToBox;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
void surf(Input IN, inout SurfaceOutputStandard o) {
float3 boxPosition = mul(_WorldToBox, float4(IN.worldPos, 1));
clip(boxPosition + 0.5);
clip(0.5 - boxPosition);
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
o.Alpha = 0.0f;
}
ENDCG
}
FallBack "Diffuse"
}
To get the world position of your pixel in the fragment shader you have to pass it through your vertex and geometry shader:
vertex => geometry
struct vertexOut {
float4 pos : SV_POSITION;
float4 color : COLOR0;
float3 normal : NORMAL;
float r : TEXCOORD0; // not sure if this is good to do lol
float3 worldPos : TEXCOORD1;
};
vertexOut vert(vertexIn i) {
vertexOut o;
...
// calculate world position
o.worldPos = mul(unity_ObjectToWorld, i.pos);
return o;
}
geometry => fragment
(Since you simply create a small triangle around the vertex you can approximate the new vertices' world positions with the one from the original vertex. If this is undesirable you have to calculate 3 separate world positions inside your loop.)
struct geomOut {
float4 pos : POSITION;
float4 color : COLO0R;
float3 normal : NORMAL;
float3 worldPos : TEXCOORD0;
};
void geom(point vertexOut IN[1], inout TriangleStream<geomOut> OutputStream) {
...
for (int i = 0; i < 3; i++) {
p[i] = mul(r,p[i]); // apply rotation
p[i].x *= _ScreenParams.y / _ScreenParams.x; // make square
OUT.pos = IN[0].pos + float4(p[i],0,0) / 2.;
// Simply use the input vertex world position. This might result in unclear cube edges.
OUT.worldPos = IN[0].worldPos;
OutputStream.Append(OUT);
}
}
Now you can add the clipping code
float3 boxPosition = mul(_WorldToBox, float4(IN.worldPos, 1));
clip(boxPosition + 0.5);
clip(0.5 - boxPosition);
and the _WorldToBox property to your fragment shader.
You also need the c# scipt passing the matrix to the shader.

Invalid subscript in unity shader

Im pretty new to shader coding but I made this one by grouping 2 unlit shaders
When I try to make this shader, which is meant to create an outline and let me interpolate 2 textures unity tells me that there is this error: invalid subscript `vertex´ at line 61
Ive tried some things but i dont get this shader to work properly, if someone knows what do i have to do id be so thankful
Shader "Unlit/Combined"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_SecondaryTex("Secondary Texture", 2D) = "white" {}
_LerpValue("Transition float", Range(0,1)) = 0.5
_OutlineColor("Outline color", color) = (0,0,0,1)
_OutlineWidth("Outline width", Range(1.0,5.0)) = 1.01
}
SubShader
{
Tags { "RenderType" = "Opaque" "Queue" = "Transparent"}
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
//float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 outvertex : SV_POSITION;
float4 pos : POSITION;
float3 normal : NORMAL;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _SecondaryTex;
float4 _SecondaryTex_ST;
float _LerpValue;
float _OutlineWidth;
float4 _OutlineColor;
v2f vert(appdata v)
{
v2f o;
v.vertex.xyz *= _OutlineWidth;
o.outvertex = UnityObjectToClipPos(v.vertex);
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// sample the texture
fixed4 col = lerp(tex2D(_MainTex, i.uv),tex2D(_SecondaryTex, i.uv), _LerpValue);
return col;
return _OutlineColor;
}
ENDCG
}
Pass//Normal render
{
ZWrite On
Material
{
Diffuse[_Color]
Ambient[_Color]
}
Lighting On
SetTexture[_MainTex]
{
ConstantColor[_Color]
}
SetTexture[_MainTex]
{
Combine previous * primary DOUBLE
}
}
}
}
The line that has a problem is line 61:
v.vertex.xyz *= _OutlineWidth;
If you look at the type that v is declared as, its declared as appdata:
v2f vert(appdata v)
If you look at the definition of appdata:
struct appdata
{
//float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
You'll see that it does not have a vertex property, only uv and normal.
So the line that's an issue should be changed to one of those, probably normal as you're trying to modify a 3-value property of it (and uv only has xy).
v.normal.xyz *= _OutlineWidth;
Alternatively you can modify appdata to have a vertex property by uncommenteing the commented out vertex:
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
Or by removing line 61, depending on what you're trying to do.

Use of UNITY_MATRIX_MV is detected

I took a billboard shader from the internet but I have this warning message that says "Shader warning in 'Custom/Billboard': Use of UNITY_MATRIX_MV is detected. To transform a vertex into view space, consider using UnityObjectToViewPos for better performance."
I'm not sure how to correct the code with the new function. Here is the shader:
Shader "Custom/Billboard"
{
Properties{
_MainTex("Texture Image", 2D) = "white" {}
_ScaleX("Scale X", Float) = 1.0
_ScaleY("Scale Y", Float) = 1.0
}
SubShader{
Tags{"Queue" = "Transparent" "RenderType" = "Transparent" }
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#pragma vertex vert
#pragma fragment frag
uniform sampler2D _MainTex;
uniform float _ScaleX;
uniform float _ScaleY;
struct vertexInput {
float4 vertex : POSITION;
float4 tex : TEXCOORD0;
};
struct vertexOutput {
float4 pos : POSITION;
float4 tex : TEXCOORD0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_P,
mul(UNITY_MATRIX_MV, float4(0,0,0,1))
+ float4(input.vertex.xyz, 0));
output.tex = input.tex;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return tex2D(_MainTex, float2(input.tex.xy));
}
ENDCG
}
}
}
You don't really need to do anything. If you do want to change it though, if nothing else just to make the warning go away, you can replace this:
mul(UNITY_MATRIX_MV, float4(0,0,0,1))
With this:
UnityObjectToViewPos((float3)0)