Unity shader find slope of vertex from center of object
Hello, I have been working on a custom shader script that blends textures based on distance from the center of the mesh object. What I am attempting to do now is to calculate the "slope" of the vertex. I'm new to shader scripting and matrix math so i figured I'd post a question and see if anyone can help me out.
What i think i need to do is first get a directional "vector" or float3 from the center of the mesh object to the vertex, then i should be able to find the dot product of the first direction against the normal direction of the vertex which should tell me the slope of said vertex. Any information as to if i am not thinking this through correctly and really how to find the directional vector from the center of the mesh to the vertex would be much appreciated.
Here is a copy of my shader script. which is currently non-functional due to the "planetdirection" not being correct. Thanks and have a great one!
Shader "Custom/FromCenter" { Properties { _CentrePoint ("Centre", Vector) = (0, 0, 0, 0)
_BlendThreshold ("Blend Distance", Float) = 0.5
_WaterTex ("Water Texture", 2D) = "white" {}
_DesertTex ("Desert Texture", 2D) = "white" {}
_ForestTex ("Forest Texture", 2D) = "white" {}
_MountainTex ("Mountain Texture", 2D) = "white" {}
_SwampTex ("Swamp Texture", 2D) = "white" {}
_ArcticTex ("Arctic Texture", 2D) = "white" {}
_PlainsTex ("Plains Texture", 2D) = "white" {}
_SwampHeight ("Swamp Height", Float) = 3
_DesertHeight ("Desert Height", Float) = 3
_PlainsHeight ("Plains Height", Float) = 3
_ForestHeight ("Forest Height", Float) = 3
_MountainHeight ("Mountain Height", Float) = 3
_ArcticHeight ("Arctic Height", Float) = 3
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
float4 _CentrePoint;
float _BlendThreshold;
float _SlopeLimt;
sampler2D _WaterTex;
sampler2D _SwampTex;
sampler2D _DesertTex;
sampler2D _PlainsTex;
sampler2D _ForestTex;
sampler2D _MountainTex;
sampler2D _ArcticTex;
float _SwampHeight;
float _DesertHeight;
float _PlainsHeight;
float _ForestHeight;
float _MountainHeight;
float _ArcticHeight;
struct Input {
float2 uv_WaterTex;
float3 worldPos;
float3 localPos;
float3 worldNormal;
INTERNAL_DATA
};
void vert (inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input,o);
o.localPos = v.vertex.xyz;
}
void surf (Input IN, inout SurfaceOutput o) {
half4 water = tex2D (_WaterTex, IN.uv_WaterTex);
half4 swamp = tex2D (_SwampTex, IN.uv_WaterTex);
half4 desert = tex2D (_DesertTex, IN.uv_WaterTex);
half4 plains = tex2D (_PlainsTex, IN.uv_WaterTex);
half4 forest = tex2D (_ForestTex, IN.uv_WaterTex);
half4 mountain = tex2D (_MountainTex, IN.uv_WaterTex);
half4 arctic = tex2D (_ArcticTex, IN.uv_WaterTex);
float dist = distance(_CentrePoint.xyz, IN.worldPos);
half4 c = water;
float3 planetDirection = _CentrePoint.xyz - IN.worldPos;
if(dot(normalize(IN.worldNormal), planetDirection) < _SlopeLimit){
c = mountain;
} else {
if (dist > _SwampHeight){
float startBlending = _SwampHeight - _BlendThreshold;
float changeFactor = saturate((dist - startBlending) / (_BlendThreshold * 2));
c = lerp(water, swamp, changeFactor);
}
if(dist > _DesertHeight){
float startBlending = _DesertHeight - _BlendThreshold;
float changeFactor = saturate((dist - startBlending) / (_BlendThreshold * 2));
c = lerp(swamp, desert, changeFactor );
}
if(dist > _PlainsHeight){
float startBlending = _PlainsHeight - _BlendThreshold;
float changeFactor = saturate((dist - startBlending) / (_BlendThreshold * 2));
c = lerp(desert, plains, changeFactor);
}
if(dist > _ForestHeight){
float startBlending = _ForestHeight - _BlendThreshold;
float changeFactor = saturate((dist - startBlending) / (_BlendThreshold * 2));
c = lerp(plains, forest, changeFactor);
}
if(dist > _MountainHeight){
float startBlending = _MountainHeight - _BlendThreshold;
float changeFactor = saturate((dist - startBlending) / (_BlendThreshold * 2));
c = lerp(forest, mountain, changeFactor);
}
if(dist > _ArcticHeight){
float startBlending = _ArcticHeight - _BlendThreshold;
float changeFactor = saturate((dist - startBlending) / (_BlendThreshold * 2));
c = lerp(mountain, arctic, changeFactor);
}
}
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Answer by rageingnonsense · Nov 08, 2015 at 06:03 PM
I have a shader just like this for a spherical terrain. If you are trying to get the slope of the terrain for the purpose of cliffs, and stuff like this, I recommend looking up the concept of Triplanar texturing. While I am not answering your question directly, I have a feelign I am answering the spirit of it. Anyways, here is a snippet from my shader (which is VERY similar to yours):
/// returns a color that is blended with larger samples of itself. used to
// reduce the appearance of tiling
fixed4 tex2DTiled(sampler2D tex, float2 uv) {
return tex2D(tex, uv) * tex2D(tex, uv * -0.1) * tex2D(tex, uv * -0.01) * 4;
}
fixed4 tex2DTriPlanar(sampler2D tex, float3 coords, float3 norm) {
float3 blending = abs(norm);
blending = normalize(max(blending, 0.00001));
float b = (blending.x + blending.y + blending.z);
blending /= float3(b, b, b);
fixed4 xaxis = tex2DTiled(tex, coords.yz);
fixed4 yaxis = tex2DTiled(tex, coords.xz);
fixed4 zaxis = tex2DTiled(tex, coords.xy);
// blend the results of the 3 planar projections.
fixed4 retVal = xaxis * blending.x + yaxis * blending.y + zaxis * blending.z;
return retVal;
}
What this does is make textures not look stretched on sloped surfaces. You use it as a drop-in replacment for tex2D(). I realize though that you need this for a different reason (althoguh I bet you'll get use out of what I posted anyways).
The way you are calculating the slope looks correct to me though:
dot(normalize(IN.worldNormal), planetDirection) < _SlopeLimit
Just remember that 1 means the normals are aligned, and 0 means that are completely perpendicular. Is there some reason that your current code is not working for this? It might be helpful to use IN.worldNormal as the output color for all of this so you can visualize the changes in slope; see if that value coming in even makes sense.
this is the line I'm having some problems with exactly
float3 planetDirection = _CentrePoint.xyz - IN.worldPos;
I'm getting an incompatible types in initialization, i assume this means that the type of variable that i am using is incorrect, but as i said before I'm very new to shader scripting and my understanding of the syntax and matrix math is at best crude.
Thnak you for the answer though and i will be checking into the triplanar texturing.
EDIT:
So I've fixed the error but I'm not getting the correct values from the slope calculation, this shader is on a sphere and so it is picking up that half the sphere is below the slope limit, which is obviously wrong, anyway i think i may have forgotten to add in a variable somewhere to make sure that its calculating from the correct direction, seems like its calculating from the sphere's general direction. But I'm unsure how to use the code you've posted, ill hold onto it though it may come in handy later on, but as of now I'm lost on how to use it haha. Thanks again.
This is because dot product for two normals will go from -1 to 1. Since you only care about 1 to 0, you can actually just use the abs() result. :)
EDIT: To elaborate on this, Your calculation will break down eventually because you will flip to the other side, and on the other side of the planet you would reverse the parameters of the subtraction. At least I think this is what is happening. In any event, the abs() wrapper should be enough to fix the problem. $$anonymous$$inda curious what happens at the poles though...
Also the code I posted for triplanar texturing is easy. Anywhere you were going to use tex2D, just use tex2DTriPlanar ins$$anonymous$$d. Easy peasy. Just need to add this function to your shader (just like you would with vert, frag etc). They are support functions.
So i tried replacing my tex2d with the triplanar function and it seems to have made them very low resolution and hard to see. What im doing with the triplanar function is this:
fixed4 water = tex2DTriPlanar(_WaterTex, IN.worldPos, IN.worldNormal);
i may be inputting a wrong param which could result in the textures messing up.
Also I'm not getting anything from the wrapping the dot product in abs its actually working worse. I thought that was the case when i first encountered the problem. It seems I'm mixing up something here lol.
Newest Script Section:
fixed4 tex2DTiled(sampler2D tex, float2 uv) {
return tex2D(tex, uv) * tex2D(tex, uv * -0.1) * tex2D(tex, uv * -0.01) * 4;
}
fixed4 tex2DTriPlanar(sampler2D tex, float3 coords, float3 norm) {
float3 blending = abs(norm);
blending = normalize(max(blending, 0.00001));
float b = (blending.x + blending.y + blending.z);
blending /= float3(b, b, b);
fixed4 xaxis = tex2DTiled(tex, coords.yz);
fixed4 yaxis = tex2DTiled(tex, coords.xz);
fixed4 zaxis = tex2DTiled(tex, coords.xy);
// blend the results of the 3 planar projections.
fixed4 retVal = xaxis * blending.x + yaxis * blending.y + zaxis * blending.z;
return retVal;
}
void surf (Input IN, inout SurfaceOutput o) {
fixed4 water = tex2DTriPlanar(_WaterTex, IN.worldPos, IN.worldNormal);
fixed4 swamp = tex2DTriPlanar(_SwampTex, IN.worldPos, IN.worldNormal);
fixed4 desert = tex2DTriPlanar(_DesertTex, IN.worldPos, IN.worldNormal);
fixed4 plains = tex2DTriPlanar(_PlainsTex, IN.worldPos, IN.worldNormal);;
fixed4 forest = tex2DTriPlanar(_ForestTex, IN.worldPos, IN.worldNormal);
fixed4 mountain = tex2DTriPlanar(_$$anonymous$$ountainTex, IN.worldPos, IN.worldNormal);
fixed4 arctic = tex2DTriPlanar(_ArcticTex, IN.worldPos, IN.worldNormal);
fixed4 cliff = tex2DTriPlanar(_CliffTex, IN.worldPos, IN.worldNormal);
float dist = distance(_CentrePoint.xyz, IN.worldPos);
fixed4 c = water;
float planetDirection = IN.localPos - _CentrePoint.xyz;
if(abs(dot(normalize(IN.worldNormal), planetDirection)) < _SlopeLimit){
c = cliff;
} else {
//all the different distance stuff...
}
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
Thanks for your time and help.
Your answer
![](https://koobas.hobune.stream/wayback/20220612044352im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Replaced '_Object2World' with 'unity_ObjectToWorld' 1 Answer
Standard Cutout Double Sided? 0 Answers
Fresnel/Edge shader problem 1 Answer
Custom shader not working on Samsung S6 1 Answer
Unity 2018 without shader editor 1 Answer