Setting o.Normal from Texture Array
I have been working on terrain shader (based on someone else's work). Currently, i have an array of textures as well as some other variables and the shader will blend textures based on the height of the terrain. So far, its all been working very well.
Ideally i would like to add bump maps to it. Even if i can't blend them i would like to be able to set the normal from the relevant texture.
In my naivety, i tried to blend set them in the same way i set the albedo but this just gave me black terrain.
It could well be that what i am attempting is impossible as there is a noticeable lack of information available, so even if your answer is something like "Thats not possible, try something else". I will still appreciate it greatly.
This is my Shader as it stands currently:
Shader "Custom/TerrainShader" {
Properties {
}
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
const static int maxLayerCount = 10;
const static float epsilon = 1E-4;
int layerCount;
float3 baseColours[maxLayerCount];
float baseStartHeights[maxLayerCount];
float baseBlends[maxLayerCount];
float baseColourStrength[maxLayerCount];
float baseTextureScales[maxLayerCount];
float minHeight;
float maxHeight;
UNITY_DECLARE_TEX2DARRAY(baseTextures);
UNITY_DECLARE_TEX2DARRAY(normals);
struct Input {
float3 worldPos;
float3 worldNormal;
INTERNAL_DATA
};
float inverseLerp(float a, float b, float value) {
return saturate((value-a)/(b-a));
}
float3 triplanarTexl(float3 worldPos, float scale, float3 blendAxes, int textureIndex) {
float3 scaledWorldPos = worldPos / scale;
float3 xProjection = UNITY_SAMPLE_TEX2DARRAY(baseTextures, float3(scaledWorldPos.y, scaledWorldPos.z, textureIndex)) * blendAxes.x;
float3 yProjection = UNITY_SAMPLE_TEX2DARRAY(baseTextures, float3(scaledWorldPos.x, scaledWorldPos.z, textureIndex)) * blendAxes.y;
float3 zProjection = UNITY_SAMPLE_TEX2DARRAY(baseTextures, float3(scaledWorldPos.x, scaledWorldPos.y, textureIndex)) * blendAxes.z;
return xProjection + yProjection + zProjection;
}
void surf (Input IN, inout SurfaceOutputStandard o) {
float heightPercent = inverseLerp(minHeight,maxHeight, IN.worldPos.y);
float3 blendAxes = abs(IN.worldNormal);
blendAxes /= blendAxes.x + blendAxes.y + blendAxes.z;
float3 albedoBlend;
for (int i = 0; i < layerCount; i ++) {
float drawStrength = inverseLerp(-baseBlends[i]/2 - epsilon, baseBlends[i]/2, heightPercent - baseStartHeights[i]);
float3 baseColour = baseColours[i] * baseColourStrength[i];
float3 textureColour = triplanarTexl(IN.worldPos, baseTextureScales[i], blendAxes, i) * (1-baseColourStrength[i]);
albedoBlend = albedoBlend * (1-drawStrength) + (baseColour+textureColour) * drawStrength;
}
o.Albedo = albedoBlend;
}
ENDCG
}
FallBack "Diffuse"
}
And this is how the variables are being set. (Ignore the try/catch, i forgot to remove that after a test i was doing before)
public void ApplyToMaterial() {
try {
material.SetInt ("layerCount", layers.Length);
material.SetColorArray ("baseColours", layers.Select(x => x.tint).ToArray());
material.SetFloatArray ("baseStartHeights", layers.Select(x => x.startHeight).ToArray());
material.SetFloatArray ("baseBlends", layers.Select(x => x.blendStrength).ToArray());
material.SetFloatArray ("baseColourStrength", layers.Select(x => x.tintStrength).ToArray());
material.SetFloatArray ("baseTextureScales", layers.Select(x => x.textureScale).ToArray());
Texture2DArray texturesArray = GenerateTextureArray (layers.Select (x => x.texture).ToArray ());
Texture2DArray normalsArray = GenerateTextureArray (layers.Select (x => x.normalMap).ToArray ());
material.SetTexture ("baseTextures", texturesArray);
material.SetTexture ("normals", normalsArray);
}
catch {
}
UpdateMeshHeights ();
}
public void UpdateMeshHeights() {
material.SetFloat ("minHeight", minHeight);
material.SetFloat ("maxHeight", maxHeight);
}
Texture2DArray GenerateTextureArray(Texture2D[] textures) {
Texture2DArray textureArray = new Texture2DArray (textureSize, textureSize, textures.Length, textureFormat, true);
for (int i = 0; i < textures.Length; i++) {
if (textures [i] != null) {
textureArray.SetPixels (textures [i].GetPixels (), i);
}
}
textureArray.Apply ();
return textureArray;
}
Your answer
![](https://koobas.hobune.stream/wayback/20220612121114im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Is there a way to fetch albedo/spec/roughness/AO and normals (RTs) in a deferred frag. shader? 0 Answers
Custom Surface Shader (from tutorial) in standard unity 3D 0 Answers
Merge(Concatenate) two textures using Shader in Unity. 0 Answers
Specular color on standard shader with metallic setup 0 Answers