I have a very simple Cutout Shader for displaying Icons in 3D space (see below).
I want to 'programatically' add an outline/stroke which follows the alpha contours, with a user defined thickness and colour.
(Left): What I currently have - an alpha cutout shader
(Right): What I want - An outline to go around the cutout
Please Note: These are not sprites, they are 3D planes
How can I go about doing this, please?
Shader "Custom/Transparent/CutoutEmissive" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
_EmissiveAmount ("Emissive Amount", Range(0,1)) = 0.5
_Outline ("Outline Thickness", Range(0,10)) = 0.0
}
SubShader {
Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
LOD 200
CGPROGRAM
#pragma surface surf Lambert alphatest:_Cutoff
sampler2D _MainTex;
fixed4 _Color;
float _EmissiveAmount;
float _Outline;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Emission = c.rgb*_EmissiveAmount;
}
ENDCG
}
Fallback "Legacy Shaders/Transparent/Cutout/VertexLit"
}
I know its very late but here it is anyways. I came across this solution where the script applies outline near the cutoff. It is hosted on git hub by José Guerreiro and it did solve my problem with cutoff meshes. The only thing I had to do is add the cutoff meshes to the list and set the color of the line. It has its thickness, intensity and cutoff. The usage is written on page itself. Downside is you will have to have the game in play mode to test this but otherwise it is really nice
Note: This outline works only with cutoff on 3d meshes and sprites; normal meshes it does not work.
Hope this helps.
Related
I made a material with a jpg on it, so the whole thing is black and white. Is there a way to change the Black parts to any color and then change the white parts to any color?
So the Black and white material could be any two colors like red and blue etc.?
The easiest way to do this is to use one channel of your texture to lerp between two other colours. This does mean that greys will come out as a colour half way between the two colours you select, which may not always be desirable.
Here's a simple Diffuse surface shader that uses this approach. You could create similar shaders for other purposes, such as UI.
Just put this shader in your project, modify your texture to be red and green, then apply this shader to the material you use to render the texture. You can then modify the properties on the material to modify what colour the two colours are rendered as.
Shader "Custom/Recolourable" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlackColor ("Black Color", Color) = (0,0,0,1)
_WhiteColor ("White Color", Color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 150
Cull Off
CGPROGRAM
#pragma surface surf Lambert noforwardadd
sampler2D _MainTex;
fixed4 _BlackColor;
fixed4 _WhiteColor;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = lerp(_BlackColor, _WhiteColor, c.r);
o.Alpha = c.a;
}
ENDCG
}
Fallback "Mobile/VertexLit"
}
i want to simulate a cube filled with "water". I want to implement a very basic solution using a texture or image, no particle animation or something. To be more clear ,if the cube is filled 40% i'd like to see the gameobject with 40% color and 60% white. Unfortunately i'm a Newbie of Unity too.. Can someone give me some hints or point me to a kind of tutorial that can help me?
Thanks for your time and answers
One option is to use a cutout shader. Below is an example:
Shader "Fade/Diffuse"
{
Properties
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
_FadeMap ("Fade Map", 2D) = "white" {}
_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque"}
LOD 300
Cull Off
CGPROGRAM
#pragma surface surf Standard
sampler2D _MainTex;
sampler2D _FadeMap;
float _Cutoff;
fixed4 _Color;
struct Input
{
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 diffuse = tex2D(_MainTex, IN.uv_MainTex) * _Color;
fixed4 fadeSample = tex2D(_FadeMap, IN.uv_MainTex);
bool cut = (fadeSample.r + fadeSample.g + fadeSample.b)/3.0 < _Cutoff ? false : true;
o.Albedo = cut ? diffuse.rgb : float3(1,1,1);
}
ENDCG
}
FallBack "Transparent/Cutout/Diffuse"
}
This shader takes 2 textures:
Color
Grayscale texture where black = cutout
One important thing about this solution is the UV mapping of your cube. If you use the standard built-in cube, you will notice that the "water level" also animates on the top and bottom of the cubes and also in the wrong direction on some of the sides. You will have to make your own cube with UVs that map the top of the cube to the black part of your fade map.
Here is the final result:
I am trying to create my first Shader in Unity3D. My goal is to add more alpha to pixels if they are close to some point in world space. But I can't get it right. My pixels are not getting smooth transparency (From min value, to max value), just or min, or max..
Here is my code:
Shader "Custom/Shield" {
Properties {
_MainTex ("Color (RGB) Alpha (A)", 2D) = "white" {}
_TexUsage ("Text usage", Range(0.1, 0.99)) = 0
_HitPoint ("Hit point", Vector) = (1, 1, 1, 1)
_Distance ("Distance", float) = 4.0
}
SubShader {
Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
// And generate the shadow pass with instancing support
#pragma surface surf Standard fullforwardshadows alpha
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
half _TexUsage;
float3 _HitPoint;
fixed _Distance;
struct Input {
float2 uv_MainTex;
float3 worldPos;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
IN.uv_MainTex.x = frac(IN.uv_MainTex.x + frac(_Time.x));
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgba;
float dist = distance(_HitPoint, IN.worldPos);
float minAlpha = 0.2;
float st = step(_Distance, dist);
float blend = (dist / _Distance) * (1 - st) + minAlpha * st;
o.Alpha = blend;
}
ENDCG
}
FallBack "Diffuse"
}
And here is a example how it works now:
But this area should be less visible, if not so close to hit point.
What am I am doing wrong?
I'm trying to code a shader similar to this one from the Unity manual which “slices” the object by discarding pixels in nearly horizontal rings via the Clip() function.
Shader "Example/Slices" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
Cull Off
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldPos;
};
sampler2D _MainTex;
sampler2D _BumpMap;
void surf (Input IN, inout SurfaceOutput o) {
clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
Fallback "Diffuse"
}
Rather than just horizontal lines I want to be able to slice at an arbitrary angle. I discovered through experimentation (since I'm new to coding shaders) that the multiplier on worldPos Z does indeed change the angle of the slice, so I set a property variable to it:
clip (frac((IN.worldPos.y+IN.worldPos.z*_MYANGLEVARIABLE) * 5) - 0.5);
This has two problems however. 1) Values up to 1.0 rotate the lines by up to 45 degrees but beyond this the lines start to go "squiggly" and convolve into all sorts of patterns rather than neat lines and 2) this only works if the face is oriented toward the positive or negative X axis. When facing Z the lines don't move and when facing Y they get bigger but don't rotate.
Changing IN.worldPos.y to IN.worldPos.x does what you might expect - similar situation but working as expected in Z rather than X.
Any ideas how to
1) Achieve arbitrary angles?
2) Have them work regardless of facing direction?
I'm using worlPos because I always want the lines to be relative to the object rather than screen space but perhaps there's another way? My actual shader is a fragment rather than a surface shader & I'm passing worldPos from the vert to the frag.
Many thanks
To get arbitrary angles you can define a plane using a normal vector and clip using the dot product.
Shader "Example/Slices" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Bumpmap", 2D) = "bump" {}
_PlaneNormal ("Plane Normal", Vector) = (0, 1, 0)
}
SubShader {
Tags { "RenderType" = "Opaque" }
Cull Off
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldPos;
};
sampler2D _MainTex;
sampler2D _BumpMap;
float3 _PlaneNormal;
void surf (Input IN, inout SurfaceOutput o) {
float d = dot(_PlaneNormal, IN.worldPos);
clip (frac(d) - 0.5);
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
Fallback "Diffuse"
}
The longer the normal vector here, the more frequent the slices.
If you want the slices to be relative to the object, you'll need to use a set of coordinates other than worldPos. Possibly this answer would help: http://answers.unity3d.com/questions/561900/get-local-position-in-surface-shader.html
Im coding a unity surface shader to slowly apply a rust effect like this:
//Take 1 base color texture.
//Take 1 rust decal texture and 1 greyscale maps.
//Take 1 float range value.
Then:
//Use the range to remove from the grayscale map all the pixels that are darker than the value itself, then make theese greysclae map the rust alpha, then apply this composited rust layer over the color texture.
I managed to do this:
void surf (Input IN, inout SurfaceOutputStandard o) {
half4 C = tex2D (_MainTex, IN.uv_MainTex); //Color Texture
half4 R = tex2D (_RustTex, IN.uv_RustTex); //Rust texture
half4 RG = tex2D (_RustGuide, IN.uv_RustGuide); //Greyscale texture
//Here i need to compose the rust layer
half4 RustResult = //??? Maybe a Clip() function or what? and how?
//Here i apply the previusly composed layer over the color texture. Already tested and working.
half4 Final = lerp (C, RustResult, RustResult.a);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
So how i can complete this shader?
I cant find a detailed documentation about the usable functuons in surface shaders.
EDIT: I almost get what i need using saturate(); functionlike the following
Properties {
_MainTex ("Base (RGB)", 2D) = "" {} //the color texture
_RustTex ("Rust Texture (RGB)", 2D) = "" {} //the rust texture
_RustGuide ("Rust Guide (A)", 2D) = "" {} //the rust greyscale texture
_RustAmount ("Rust Amount", range(0.0, 1.0)) = 0.0 //the rust amount float value
_RustMultiplier ("Rust Multiplier", float) = 2
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma target 3.0
#include "UnityPBSLighting.cginc"
#pragma surface surf Standard
sampler2D _MainTex;
sampler2D _RustTex;
sampler2D _RustGuide;
float _RustAmount;
float _RustMultiplier;
struct Input {
float2 uv_MainTex;
float2 uv_RustTex;
float2 uv_RustGuide;
};
void surf (Input IN, inout SurfaceOutputStandard o) {
half4 M = tex2D (_MainTex, IN.uv_MainTex);
half4 R = tex2D (_RustTex, IN.uv_RustTex);
half4 RG = tex2D (_RustGuide, IN.uv_RustGuide);
half4 RustResult;
RustResult.rgb = R.rgb;
if (_RustAmount > 0) {
RustResult.a = trunc(saturate(RG.a * _RustAmount * _RustMultiplier);
}
half4 Final = lerp (M, RustResult, RustResult.a);
o.Albedo = Final.rgb;
o.Alpha = Final.a;
}
ENDCG
}
FallBack Off
}
This makes the effect that I need. The only problem now is how i can blur the edges of the alpha?
Use the range to remove from the grayscale map all the pixels that are
darker than the value itself
Can't you simply clamp the values below _RustAmount float? Something like:
float greyScaleMapValue = tex2D(_RustGuide, IN.uv_RustGuide).a; //assuming rust guide is stored as a single channel
float clampedMap = clamp(greyScaleMapValue , _RustAmount, 1); //clamped map stores a value clamped between _RustAmount and 1 -> every pixel darker than _RustAmount are 0
half3 albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
half3 rust = tex2D (_RustTex, IN.uv_RustTex).rgb;
float3 finalCol = lerp(albedo, rust, clampedMap); // all values in the map below _RustAmount will have plain albedo value, the other will be blended with rust using the map
return float4(finalCol,1);
Note that the code above produce some abrupt transition from texture to rust (more abrupt more _RustmAmount is higher than zero). You want eventually to remap each value higher than zero after the clamp in [0,1] range.
If you need to smooth the transition you can remap the interval [_RustAmount,1] into [0,1]:
float clampedMapNormalized = (clampedMap - _RustAmount) / (1 - _RustAmount);
Hope this helps
Side note:
avoid branching in the shaders (even if branching on uniforms shouldn't be such a pain on modern hardware)
if the map uses the same sets of uv coordinates (and tiling)of one of the other 2 textures, than you can pack it inside the relative alpha channel using one less texture sample operation.
since your shader is opaque, I guess final alpha value isn't relevant, so I just used a float3 to minimize the values to be lerped.