Description
I'm trying to create a water shader, and at this point in development I'm using a Photoshop-generated cloud texture as a map for vertex displacement.
The displacement itself works just fine, but there is no shading on the mesh.
In Game view:
In Scene view:
Without the Point Light, the whole mesh is just a grey blob with detectable shapes only at the edges, where the color intersects with the grey-brown of the background:
Remarks & Code
Because I need more vertices than the default Plane object has, I decided to generate the mesh inside a script. To do this, I followed Brackeys' tutorial on procedural terrain generation; the mesh generation part.
This includes generating the vertices, the triangles, and the UVs. Here is my code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class PlaneMeshGenerator : MonoBehaviour {
public Vector2 squareCount;
public int resolution = 4;
[HideInInspector] public Mesh mesh;
private Vector3[] vertices;
private int[] triangles;
private Vector2[] uv;
private int xSize;
private int zSize;
private void Awake() {
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
}
private void Start() {
xSize = (int)squareCount.x;
zSize = (int)squareCount.y;
GenerateMeshData();
UpdateMesh();
}
private void GenerateMeshData() {
xSize = (int)squareCount.x;
zSize = (int)squareCount.y;
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
triangles = new int[(xSize * zSize * 6)];
uv = new Vector2[(xSize + 1) * (zSize + 1)];
#region Generate the Vertices
for (int i = 0, z = -(zSize / 2); z < zSize / 2 + 1; z++) {
for (int x = -(xSize / 2); x < xSize / 2 + 1; x++) {
vertices[i++] = new Vector3(x / (float)resolution, 0f, z / (float)resolution);
}
}
#endregion
#region Generate the Triangles
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++) {
for (int x = 0; x < xSize; x++) {
triangles[tris + 0] = vert;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert += 1;
tris += 6;
}
vert += 1;
}
#endregion
#region Generate the UVs
for (int i = 0, z = 0; z < zSize + 1; z++) {
for (int x = 0; x < xSize + 1; x++) {
uv[i++] = new Vector2((float)x / xSize, (float)z / zSize);
}
}
#endregion
}
private void UpdateMesh() {
Mesh mesh = this.mesh;
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uv;
mesh.RecalculateNormals();
}
}
(You can disregard the resolution
variable. It just resizes the mesh so I can get more vertices for the same size.)
And here is the shader code:
Shader "Custom/Water"
{
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
_CloudTex ("Clouds Texture", 2D) = "white" {}
_Amount ("Displacement Amount", Range(0.1, 5)) = 1.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 vertex:vert
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _CloudTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
half _Amount;
// 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 vert(inout appdata_full v, out Input o) {
UNITY_INITIALIZE_OUTPUT(Input,o);
fixed height = tex2Dlod(_CloudTex, float4(v.texcoord.xy, 0.0, 0.0)).r;
v.vertex.xyz += v.normal * height * _Amount;
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _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"
}
My Guess
At first I thought the normals had been calculated incorrectly, but I don't think that's the case now. For one, applying an image texture works perfectly fine. And as you can see, I'm using the vertex normals in my shader code for displacement, and that works fine as well.
Cast and Receive Shadows is on, and the Directional Light's culling mask is set to "Everything".
The shader was created as a Standard Surface Shader, not Unlit, so that shouldn't be a problem.
A while ago, I followed the aforementioned tutorial to the fullest, and I didn't have this problem there. The only difference I can see here is that I took the displacement code from the C# script to the shader, meaning I used a different material. However, I don't know how to fix this without reverting to doing this in the script, which I can't do here.
Said old proc. gen. terrain:
Other
I don't know what Shadowmasks and Shadow Cascades are, but I took pictures anyway, in case they're relevant":