I use the following shader in Unity.
Ring like sonar is spreading from the place where game objects collided.
With reference to the underlying image.
I would like to add emission to make this ring visible even in the dark, but I am new to shaders. I am in trouble because I do not know where to change.
The setting for making it dark is as follows.
Set the Intensity Multiplier of Environment Lightning to 0.
Remove skybox.
Set the Background of Camera to black.
The shader I use is below.
Shader "MadeByProfessorOakie/SimpleSonarShader" {
Properties{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Glossiness("Smoothness", Range(0,1)) = 0.5
_Metallic("Metallic", Range(0,1)) = 0.0
_RingColor("Ring Color", Color) = (1,1,1,1)
_RingColorIntensity("Ring Color Intensity", float) = 2
_RingSpeed("Ring Speed", float) = 1
_RingWidth("Ring Width", float) = 0.1
_RingIntensityScale("Ring Range", float) = 1
_RingTex("Ring Texture", 2D) = "white" {}
}
SubShader{
Tags{ "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _RingTex;
struct Input {
float2 uv_MainTex;
float3 worldPos;
float3 uv_Illum;
};
// The size of these arrays is the number of rings that can be rendered at once.
// If you want to change this, you must also change QueueSize in SimpleSonarShader_Parent.cs
half4 _hitPts[20];
half _StartTime;
half _Intensity[20];
half _Glossiness;
half _Metallic;
fixed4 _Color;
fixed4 _RingColor;
// 追加
fixed4 _EmissionLM;
half _RingColorIntensity;
half _RingSpeed;
half _RingWidth;
half _RingIntensityScale;
void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
half DiffFromRingCol = abs(o.Albedo.r - _RingColor.r) + abs(o.Albedo.b - _RingColor.b) + abs(o.Albedo.g - _RingColor.g);
// Check every point in the array
// The goal is to set RGB to highest possible values based on current sonar rings
for (int i = 0; i < 20; i++) {
half d = distance(_hitPts[i], IN.worldPos);
half intensity = _Intensity[i] * _RingIntensityScale;
half val = (1 - (d / intensity));
if (d < (_Time.y - _hitPts[i].w) * _RingSpeed && d >(_Time.y - _hitPts[i].w) * _RingSpeed - _RingWidth && val > 0) {
half posInRing = (d - ((_Time.y - _hitPts[i].w) * _RingSpeed - _RingWidth)) / _RingWidth;
// Calculate predicted RGB values sampling the texture radially
float angle = acos(dot(normalize(IN.worldPos - _hitPts[i]), float3(1,0,0)));
val *= tex2D(_RingTex, half2(1 - posInRing, angle));
half3 tmp = _RingColor * val + c * (1 - val);
// Determine if predicted values will be closer to the Ring color
half tempDiffFromRingCol = abs(tmp.r - _RingColor.r) + abs(tmp.b - _RingColor.b) + abs(tmp.g - _RingColor.g);
if (tempDiffFromRingCol < DiffFromRingCol)
{
// Update values using our predicted ones.
DiffFromRingCol = tempDiffFromRingCol;
o.Albedo.r = tmp.r;
o.Albedo.g = tmp.g;
o.Albedo.b = tmp.b;
o.Albedo.rgb *= _RingColorIntensity;
}
}
}
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
}
ENDCG
}
FallBack "Diffuse"
}
It's always worth to have a look at the Built-In shaders when you create custom shaders.
Just pick those which behave the way you want and see what they do.
Here is an example for the BlinnPhong surface:
//Properties
_EmissionLM ("Emission (Lightmapper)", Float) = 0
[Toggle] _DynamicEmissionLM ("Dynamic Emission (Lightmapper)", Int) = 0
//Output
o.Emission = c.rgb * tex2D(_Illum, IN.uv_Illum).a;
Hope it helps you
The problem was solved. I rewrote the code as follows. Thank you very much for those who thought.
I rewrote as follow.
// Determine if predicted values will be closer to the Ring color
half tempDiffFromRingCol = abs(tmp.r - _RingColor.r) + abs(tmp.b - _RingColor.b) + abs(tmp.g - _RingColor.g);
if (tempDiffFromRingCol < DiffFromRingCol)
{
// Update values using our predicted ones.
//DiffFromRingCol = tempDiffFromRingCol;
/*
o.Albedo.r = tmp.r;
o.Albedo.g = tmp.g;
o.Albedo.b = tmp.b;
o.Albedo.rgb *= _RingColorIntensity;
*/
// I Changed here
o.Emission = tmp;
}
Related
I'm new to 3D code and writing shaders so I'm probably doing something stupid here. I'm working on a simple Minecraft clone just to learn Unity, and I thought the easiest way to texture the ground would be to use a texture atlas with a grass + dirt texture and use a shader to choose which part of the atlas to get the texture from depending on the surface normal. i.e. for the face of a cube that points upwards, the grass texture will be used, and for all other faces the dirt texture will be used. The texture looks like this:
In my shader, if I do the following, I get the dirt texture on every face as expected:
float y = (IN.uv_MainTex.y / 2.0f);
float2 uv = {IN.uv_MainTex.x, y};
fixed4 c = tex2D (_MainTex, uv) * _Color;
o.Albedo = c.rgb;
If I use the same code but always add 0.5f to y to get the top half, i.e. float y = (IN.uv_MainTex.y / 2.0f) + 0.5f; I get the grass texture everywhere as expected:
But when I try to set y based on the normal, i.e. float y = (IN.uv_MainTex.y / 2.0f) + floor(IN.worldNormal.y) * 0.5f; I get this weird result where the top face is mostly grass but with parts of the dirt texture showing in diagonal lines:
Is IN.worldNormal the right normal to be using, or do I need to transform it into some other space? Or is the problem with how I'm using floor(), maybe? Any advice is appreciated.
Full shader code:
Shader "Custom/GroundShader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_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
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
float3 worldNormal;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
float y = (IN.uv_MainTex.y / 2.0f) + floor(IN.worldNormal.y) * 0.5f;
float2 uv = {IN.uv_MainTex.x, y};
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, uv) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Edit: the original answer is below, but I was approaching this problem the wrong way. The correct way to do this is just to set the UVs of the mesh using the Mesh.uv property. There is no need to use a shader in this case.
Copying an answer I got from Namey5 on the Unity forum:
This looks to be a precision issue (potentially from vertex normal
interpolation). There are probably smarter ways to do this, but as a
simple fix you could just add a small bias to the normal, i.e;
float y = (IN.uv_MainTex.y / 2.0f) + floor (IN.worldNormal.y + 0.01) * 0.5f;
I modified this slightly to float y = (IN.uv_MainTex.y / 2.0f) + floor (IN.worldNormal.y * 1.1f) * 0.5f; This ensures the bottom face also shows the dirt texture because the normal will get floored down to -2 instead of -1.
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
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.
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.