Skip to content

Commit

Permalink
Added a Mesh Normals propset with a dropdown to select from three opt…
Browse files Browse the repository at this point in the history
…ions for always-pixel, always-vertex, or pick-by-distance (the default). Added defines to main.glsl so when normals are calculated always-vertex, it completely bypasses the call to get_normal and normalizes v_normal inline. Added a define to get_normal so always-vertex or by-distance will both check for region edges, but by-dstance also checks for range. Added support for calculating noise height on the cpu.
  • Loading branch information
FishOfTheNorthStar committed May 10, 2024
1 parent ea1db16 commit 998ffd9
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 87 deletions.
2 changes: 2 additions & 0 deletions Terrain3D.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
<ClInclude Include="src\propsets\__PROPSETS.h" />
<ClInclude Include="src\register_types.h" />
<ClInclude Include="src\propsets\_BLENDING.h" />
<ClInclude Include="src\propsets\_NORMALS.h" />
<ClInclude Include="src\terrain_3d.h" />
<ClInclude Include="src\terrain_3d_editor.h" />
<ClInclude Include="src\logger.h" />
Expand Down Expand Up @@ -204,6 +205,7 @@
<None Include="project\addons\terrain_3d\shader\core\t3d_standard_header.gdshaderinc" />
<None Include="project\addons\terrain_3d\shader\mods\t3d_debug_view.gdshaderinc" />
<None Include="project\addons\terrain_3d\shader\mods\t3d_uv_distort_funcs.gdshaderinc" />
<None Include="project\addons\terrain_3d\shader\vertex\t3d_bg_world_noise_type1_funcs.gdshaderinc" />
<None Include="project\addons\terrain_3d\shader\vertex\t3d_vertex_apply_standard_varyings.gdshaderinc" />
<None Include="project\addons\terrain_3d\shader\vertex\t3d_vertex_early_discard_test.gdshaderinc" />
<None Include="project\addons\terrain_3d\shader\vertex\t3d_vertex_init_varyings.gdshaderinc" />
Expand Down
2 changes: 1 addition & 1 deletion doc/docs/shader_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Finally `vertex()` sets the UV and UV2 coordinates, and the height of the mesh v

The first step is calculating the terrain normals. This shared between the `vertex()` and `fragment()` functions. Clipmap terrain vertices spread out at lower LODs causing certain things like normals look strange when you look in the distance as the vertices used for calculation suddenly separate at further LODs. So we switch from normals calculated per vertex to per pixel when the pixel is farther than `vertex_normal_distance`.

The exact distance that the transition from per vertex to per pixel normal calculations occurs can be adjusted from the default of 192m via the `vertex_normals_distance` uniform.
The exact distance that the transition from per vertex to per pixel normal calculations occurs can be adjusted from the default of 192m via the `_normals_distance` uniform.

Generating normals in the shader works fine and modern GPUs can handle the load of 2 - 3 additional height lookups and the on-the-fly calculations. Doing this saves 3-4MB VRAM per region.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ uniform uint _mouse_layer = 0x80000000u; // Layer 32
// The height-based or default blend sharpness value
uniform float _blending_sharpness = 0.87;

// The distance beyond which normals will be calculated per-pixel, if
// NORMALS_BY_DISTANCE is defined.
uniform float _normals_distance;

// *****************************************************************************
// END: CORE INTERNALS
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -99,13 +103,4 @@ uniform float _blending_sharpness = 0.87;
// *****************************************************************************
// END: MOD UNIFORMS
// -----------------------------------------------------------------------------
// BEGIN: UNSORTED STAGING AREA
// *****************************************************************************

// Public uniforms
uniform float vertex_normals_distance : hint_range(0, 1024) = 128.0;

// *****************************************************************************
// END: UNSORTED STAGING AREA
// -----------------------------------------------------------------------------
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// WORLD_NOISE1

//float hashv2(vec2 v) {
// return fract(1e4 * sin(17.0 * v.x + v.y * 0.1) * (0.1 + abs(sin(v.y * 13.0 + v.x)))); }

// Explicitly Optimized - changes made should have (mostly) already been inlined by the compiler but why take chances.
float ihashv2(ivec2 iv) {
vec2 v = sin( vec2( iv * ivec2(17, 13) ) + vec2( float(iv.y) * 0.1, float(iv.x) ) );
v.y = ( abs(v.y) + 0.1 );
return fract( 1e4 * v.x * v.y ); }

// alternate optimization, unclear which performs better between this and above.
//float hashv2(ivec2 iv) {
// vec2 v = vec2(iv);
// v = sin( vec2(
// fma( 17., v.x, v.y * 0.1 ),
// fma( 13., v.y, v.x ) ) );
// return fract( 1e4 * v.x * ( 0.1 + abs(v.y) ) ); }

/* vec3 orig_noise2D(vec2 x) {
vec2 f = fract(x);
// Quintic Hermine Curve. Similar to SmoothStep()
vec2 u = f*f*f*(f*(f*6.0-15.0)+10.0);
vec2 du = 30.0*f*f*((f*f)-(f*2.0)+1.0);
// vec2 du = 30.0*f*f*(f*(f-2.0)+1.0); // Original

vec2 p = floor(x);

// Four corners in 2D of a tile
float a = hashv2( p+vec2(0,0) );
float b = hashv2( p+vec2(1,0) );
float c = hashv2( p+vec2(0,1) );
float d = hashv2( p+vec2(1,1) );

// Mix 4 corner percentages
float k0 = a;
float k1 = b - a;
float k2 = c - a;
float k3 = a - b - c + d;
return vec3( k0 + k1 * u.x + k2 * u.y + k3 * u.x * u.y,
du * ( vec2(k1, k2) + k3 * u.yx) ); } */

// https://iquilezles.org/articles/morenoise/
// Explicitly Optimized - changes made should already be in effect from compiler optimization but that's not always the case.
vec3 noise2D(vec2 x) {
const ivec2 i01 = ivec2(0,1);

vec2 f = fract(x);
// Quintic Hermine Curve. Similar to SmoothStep()
vec2 f2 = f*f;
vec4 udu = f2.xyxy * vec4(
f * (fma(f, (fma(f, vec2(6.0), -vec2(15.0))), vec2(10.0))),
30.0 * (fma (f, (f - 2.0), vec2(1.0))));
ivec2 p = ivec2(floor(x));

// Four corners in 2D of a tile
vec4 abcd = vec4( ihashv2(p), ihashv2(p + i01.yx), ihashv2(p + i01.xy), ihashv2(p + 1) );

// Mix 4 corner percentages
vec3 k123 = abcd.yzx - abcd.xxy;
k123.z = k123.z - abcd.z + abcd.w;
udu.zw *= fma(udu.yx, k123.zz, k123.xy);
k123 *= udu.xyx;
return vec3( fma(k123.z, udu.y, abcd.x + k123.x + k123.y), udu.zw ); }

float bg_world(vec2 p) {
float a = 0.0;
float b = 1.0;
vec2 d = vec2(0.0);
int octaves = int( clamp(
float(_bg_world_max_octaves) - floor(v_vertex_xz_dist/(_bg_world_lod_distance)),
float(_bg_world_min_octaves), float(_bg_world_max_octaves)) );
for( int i=0; i < octaves; i++ ) {
vec3 n = noise2D(p);
d += n.yz;
a += b * n.x / (1.0 + dot(d,d));
b *= 0.5;
p = mat2( vec2(0.8, -0.6), vec2(0.6, 0.8) ) * p * 2.0; }
return a; }

// World Noise end
36 changes: 22 additions & 14 deletions src/propsets/ALL_SETS.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "propsets/_TINTING.h"
#include "propsets/_AUTO_TEXTURING.h"
#include "propsets/_UV_DISTORTION.h"
#include "propsets/_NORMALS.h"
#include "propsets/_DEBUG_VIEWS.h"

// ################################################################################
Expand Down Expand Up @@ -70,31 +71,34 @@ within the generated shader, so if ever this happens it's not a big deal.
// section. It defines the functions that will provide the values to the placeholder
// in-line "help" textareas.
#define __MAKE_MANAGED_HELP()\
HELP_BLEND()\
HELP_MULTI()\
HELP_BG_WORLD()\
HELP_BLEND()\
HELP_NORMS()\
HELP_TINTING()\
HELP_MULTI()\
HELP_AUTO_TEXTURING()\
HELP_UVDIST()\
HELP_DEBUG()\
HELP_SHADER_OVERRIDE()

// This generates all of the actual functions within the class .cpp file.
#define MAKE_MANAGED_FUNCTIONS()\
MAKE_BLENDING_FUNCTIONS()\
MAKE_MULTI_SCALING_FUNCTIONS()\
MAKE_BG_WORLD_FUNCTIONS()\
MAKE_BLENDING_FUNCTIONS()\
MAKE_NORMALS_FUNCTIONS()\
MAKE_TINTING_FUNCTIONS()\
MAKE_MULTI_SCALING_FUNCTIONS()\
MAKE_AUTO_TEXTURING_FUNCTIONS()\
MAKE_UV_DISTORTION_FUNCTIONS()\
MAKE_DEBUG_VIEW_FUNCTIONS()

// This binds the necessary get/set functions for each managed property group.
#define BIND_MANAGED_VARS()\
BIND_BLENDING_VARS()\
BIND_MULTI_SCALING_VARS()\
BIND_BG_WORLD_VARS()\
BIND_BLENDING_VARS()\
BIND_NORMALS_VARS()\
BIND_TINTING_VARS()\
BIND_MULTI_SCALING_VARS()\
BIND_AUTO_TEXTURING_VARS()\
BIND_UV_DISTORTION_VARS()\
BIND_DEBUG_VIEW_VARS()
Expand All @@ -103,38 +107,42 @@ within the generated shader, so if ever this happens it's not a big deal.
// it's probably best to call this after BIND_MANAGED_VARS, but I'm not sure
// it really matters.
#define ADD_MANAGED_PROPS()\
PROPS_BLENDING()\
PROPS_MULTI_SCALING()\
PROPS_BG_WORLD()\
PROPS_BLENDING()\
PROPS_NORMALS()\
PROPS_TINTING()\
PROPS_MULTI_SCALING()\
PROPS_AUTO_TEXTURING()\
PROPS_UV_DISTORTION()\
PROPS_DEBUG_VIEW()

#define PRIVATE_MANAGED_VARS()\
PRIV_BLENDING_VARS()\
PRIV_MULTI_SCALING_VARS()\
PRIV_BG_WORLD_VARS()\
PRIV_BLENDING_VARS()\
PRIV_NORMALS_VARS()\
PRIV_TINTING_VARS()\
PRIV_MULTI_SCALING_VARS()\
PRIV_AUTO_TEXTURING_VARS()\
PRIV_UV_DISTORTION_VARS()\
PRIV_DEBUG_VIEW_VARS()

#define PUBLIC_MANAGED_FUNCS()\
__MAKE_MANAGED_HELP()\
PUBLIC_BLENDING_FUNCS()\
PUBLIC_MULTI_SCALING_FUNCS()\
PUBLIC_BG_WORLD_FUNCS()\
PUBLIC_BLENDING_FUNCS()\
PUBLIC_NORMALS_FUNCS()\
PUBLIC_TINTING_FUNCS()\
PUBLIC_MULTI_SCALING_FUNCS()\
PUBLIC_AUTO_TEXTURING_FUNCS()\
PUBLIC_UV_DISTORTION_FUNCS()\
PUBLIC_DEBUG_VIEW_FUNCS()

#define UPDATE_MANAGED_VARS()\
UPDATE_BLENDING()\
UPDATE_MULTI_SCALING()\
UPDATE_BG_WORLD()\
UPDATE_BLENDING()\
UPDATE_NORMALS()\
UPDATE_TINTING()\
UPDATE_MULTI_SCALING()\
UPDATE_AUTO_TEXTURING()\
UPDATE_UV_DISTORTION()

Expand Down
73 changes: 73 additions & 0 deletions src/propsets/_NORMALS.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once

#ifndef TERRAIN3D_NORMALS_H
#define TERRAIN3D_NORMALS_H

//Code Templates for Terrain3D Managed GLSL/Godot NORMALS Properties

// ################################################################################
#pragma region _NORMALS_

// *******************
// ** NORMALS **
// *******************

#pragma region _HELP_
#define HELP_NORMS()\
ADD_HELP_TOPIC_DECL(normals, R"(
A mesh normal is the outward facing direction of a surface at any point.
3D graphics smoothly shifts that normal over the entire surface. How
that normal is calculated impacts quality and speed inversely, with
per-pixel providing the highest quality, but per-vertex having the fastest
speed. A third option is available, where beyond a certain distance
it uses per-pixel, because the mesh density is much lower there and it
looks better per-pixel. But up-close where the mesh density is very high,
per-pixel is less necessary. It still looks better but it's harder to tell
and in many situations it might be good enough, and offer faster speeds.

The Distance setting lets you adjust the vertex/pixel range if the By_Distance
option is selected.
)")
#pragma endregion _HELP_

#pragma region _normals_helper_macros_
#define GETR_NORMS(m_bind_id, m_type) GSTR( normals_##m_bind_id, m_type )
#define VAR_NORMS(m_bind_id, m_type, m_value) VARR( normals_##m_bind_id, m_type##, m_value )
#define PROP_NORMS(m_member_id, m_var_type, ...) __PROP_GENGRP( normals, m_member_id, m_var_type, __VA_ARGS__ )
#define BIND_NORMS(m_bind_id, m_param_label) ADD_BIND( normals_##m_bind_id, m_param_label )
#define SETUPD_NORMS(m_bind_id, m_type, s_log_desc) SETUPD__GRP( normals, normals_##m_bind_id, m_type, s_log_desc )
#define _SETPR_NORMS(m_bind_id, m_type, s_log_desc) __SETPR_GRP( normals, normals_##m_bind_id, m_type, s_log_desc )
#define _UPDSH_NORMS(m_member_id) UPDATE_SHADER( normals_##m_member_id )
#pragma endregion _normals_helper_macros_

#define UPDATE_NORMALS() UPDATE_GROUP(normals)

#define PRIV_NORMALS_VARS()\
GROUP_VARS(normals)\
VAR_NORMS(quality, NormalCalculation, BY_DISTANCE)\
VAR_NORMS(distance, float, 128.0f)

#define PUBLIC_NORMALS_FUNCS()\
GETR_NORMS(quality, NormalCalculation)\
GETR_NORMS(distance, float)

#define BIND_NORMALS_VARS() \
BIND_NORMS(quality, strategy)\
BIND_NORMS(distance, range)

#define PROPS_NORMALS()\
ADD_GROUP("Mesh Normals", "normals_");\
PROP_NORMS(quality, INT, ENUM, "Pixel,Vertex,By_Distance")\
PROP_NORMS(distance, FLOAT, RANGE, "0.0,1024.0,1., or_greater")\
ADD_INLINE_HELP(normals, Mesh Normals, About Mesh Normals)

#define MAKE_NORMALS_FUNCTIONS() \
UPDATE_GRP_START_CON(normals, true)\
_UPDSH_NORMS(distance)\
UPDATE_GRP_END()\
SETUPD_NORMS(quality, NormalCalculation, "Quality: ")\
_SETPR_NORMS(distance, float, "Distance: ")

#pragma endregion _NORMALS_
// ################################################################################
#endif
Loading

0 comments on commit 998ffd9

Please sign in to comment.