Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create minimum_lowpoly.gdshader #609

Closed
wants to merge 0 commits into from

Conversation

CrowhopTech
Copy link
Contributor

@CrowhopTech CrowhopTech commented Jan 30, 2025

Fixes #422.

Based on the existing minimum.gdshader, but with the lowpoly modifications suggested in #422. Also adds a colormap version entirely provided based on the comments here: thanks @Xtarsia !

With the minimal.gdshader in the current main of the repo:
image

With this modified minimal_lowpoly.gdshader:
image

With minimum_lowpoly_colormap.gdshader:
image

@Saul2022
Copy link

Pretty cool, how is the performance difference?

@CrowhopTech
Copy link
Contributor Author

I am very inexperienced with performance topics in games admittedly, but I'll be doing some random experiments today and will see if I can learn how to do performance measurements on the way!

@TokisanGames
Copy link
Owner

This is a useful contribution, but to complete #422 it should include texturing, with the unnecessary features of the main shader stripped out as described at the bottom here #422 (comment).

This could be done without textures, using the colormap. That's an acceptable solution. So that needs to be included in the shader, as well as documented instructions in the shader comments so they don't try adding textures.

image

@Xtarsia
Copy link
Contributor

Xtarsia commented Jan 31, 2025

@CrowhopTech I belive this would be a good example:

shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_back,diffuse_burley,specular_schlick_ggx,skip_vertex_transform;

/* This shader is an example of a minimal, low-poly style.
 * The terrain function depends on this shader. So don't change:
 * - vertex positioning in vertex()
 * - terrain normal calculation in fragment()
 *
 * Uniforms that begin with _ are private and will not display in the inspector. However, 
 * you can set them via code. You are welcome to create more of your own hidden uniforms.
 *
 * This shader only supports the color map.
 *
 */

// Defined Constants
#define SKIP_PASS 0
#define VERTEX_PASS 1
#define FRAGMENT_PASS 2

// Private uniforms
uniform uint _background_mode = 1u; // NONE = 0, FLAT = 1, NOISE = 2
uniform uint _mouse_layer = 0x80000000u; // Layer 32
uniform float _vertex_spacing = 1.0;
uniform float _vertex_density = 1.0; // = 1/_vertex_spacing
uniform float _region_size = 1024.0;
uniform float _region_texel_size = 0.0009765625; // = 1/1024
uniform int _region_map_size = 32;
uniform int _region_map[1024];
uniform vec2 _region_locations[1024];
uniform highp sampler2DArray _height_maps : repeat_disable;
uniform highp usampler2DArray _control_maps : repeat_disable;
uniform highp sampler2DArray _color_maps : source_color, filter_nearest_mipmap, repeat_disable;

// Public uniforms
uniform float default_roughness : hint_range(0.0, 1.0, 0.01) = 0.8;

varying flat vec3 v_camera_pos; // required for editor functions
varying vec3 v_vertex; // required for editor functions
varying flat vec2 v_uv_offset;
varying flat vec2 v_uv2_offset;

////////////////////////
// Vertex
////////////////////////

// Takes in UV world space coordinates & search depth (only applicable for background mode none)
// Returns ivec3 with:
// XY: (0 to _region_size) coordinates within a region
// Z: layer index used for texturearrays, -1 if not in a region
ivec3 get_region_uv(const vec2 uv, const int search) {
	vec2 r_uv = uv;
	vec2 o_uv = mod(r_uv,_region_size);
	ivec2 pos;
	int bounds, layer_index = -1;
	for (int i = -1; i < clamp(search, SKIP_PASS, FRAGMENT_PASS); i++) {
		if ((layer_index == -1 && _background_mode == 0u ) || i < 0) {
			r_uv -= i == -1 ? vec2(0.0) : vec2(float(o_uv.x <= o_uv.y), float(o_uv.y <= o_uv.x));
			pos = ivec2(floor((r_uv) * _region_texel_size)) + (_region_map_size / 2);
			bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
			layer_index = (_region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1);
		}
	}
	return ivec3(ivec2(mod(r_uv,_region_size)), layer_index);
}

// Takes in UV2 region space coordinates, returns vec3 with:
// XY: (0 to 1) coordinates within a region
// Z: layer index used for texturearrays, -1 if not in a region
vec3 get_region_uv2(const vec2 uv2) {
	// Remove texel offset to ensure correct region index
	ivec2 pos = ivec2(floor(uv2 - vec2(_region_texel_size * 0.5))) + (_region_map_size / 2);
	int bounds = int(uint(pos.x | pos.y) < uint(_region_map_size));
	int layer_index = _region_map[ pos.y * _region_map_size + pos.x ] * bounds - 1;
	return vec3(uv2 - _region_locations[layer_index], float(layer_index));
}

void vertex() {
	// Get camera pos in world vertex coords
	v_camera_pos = INV_VIEW_MATRIX[3].xyz;

	// Get vertex of flat plane in world coordinates and set world UV
	v_vertex = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;

	// UV coordinates in world space. Values are 0 to _region_size within regions
	UV = round(v_vertex.xz * _vertex_density);

	// UV coordinates in region space + texel offset. Values are 0 to 1 within regions
	UV2 = fma(UV, vec2(_region_texel_size), vec2(0.5 * _region_texel_size));

	// Discard vertices for Holes. 1 lookup
	ivec3 region = get_region_uv(UV, VERTEX_PASS);
	uint control = texelFetch(_control_maps, region, 0).r;
	bool hole = bool(control >>2u & 0x1u);

	// Show holes to all cameras except mouse camera (on exactly 1 layer)
	if ( !(CAMERA_VISIBLE_LAYERS == _mouse_layer) && 
			(hole || (_background_mode == 0u && region.z < 0))) {
		v_vertex.x = 0. / 0.;
	} else {
		// Set final vertex height & calculate vertex normals. 3 lookups
		v_vertex.y = texelFetch(_height_maps, region, 0).r;
	}
	
	// Transform UVs to local to avoid poor precision during varying interpolation.
	v_uv_offset = MODEL_MATRIX[3].xz * _vertex_density;
	UV -= v_uv_offset;
	v_uv2_offset = v_uv_offset * _region_texel_size;
	UV2 -= v_uv2_offset;

	// Convert model space to view space w/ skip_vertex_transform render mode
	VERTEX = (VIEW_MATRIX * vec4(v_vertex, 1.0)).xyz;
	NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
	BINORMAL = normalize((MODELVIEW_MATRIX * vec4(BINORMAL, 0.0)).xyz);
	TANGENT = normalize((MODELVIEW_MATRIX * vec4(TANGENT, 0.0)).xyz);
}

////////////////////////
// Fragment
////////////////////////

void fragment() {
	// Recover UVs
	vec2 uv = UV + v_uv_offset;
	vec2 uv2 = UV2 + v_uv2_offset;
	
	// Apply terrain normals
	NORMAL = normalize(cross(dFdyCoarse(VERTEX),dFdxCoarse(VERTEX)));
	TANGENT = normalize(cross(NORMAL, vec3(0.0, 0.0, 1.0)));
	BINORMAL = normalize(cross(NORMAL, TANGENT));
	
	// Determine if we're in a region or not (region_uv.z>0)
	vec3 region_uv = get_region_uv2(uv2);
	
	// Colormap. 1 lookup
	vec4 color_map = vec4(1., 1., 1., .5);
	if (region_uv.z >= 0.) {
		float lod = textureQueryLod(_color_maps, uv2.xy).y;
		color_map = textureLod(_color_maps, region_uv, lod);
	}
	
	// Wetness/roughness modifier, converting 0 - 1 range to -1 to 1 range
	float roughness = fma(color_map.a - 0.5, 2.0, default_roughness);
	
	// Apply PBR
	ALBEDO = color_map.rgb;
	ROUGHNESS = roughness;
	SPECULAR = 1.0 - roughness;

}

@CrowhopTech
Copy link
Contributor Author

Agreed! I had already been thinking about contributing my hacked textured shader, but I feel better with your "official" guidance. I'll take a crack at that tonight, thanks!

@CrowhopTech
Copy link
Contributor Author

Yes, the color map shader you provided works out of the box 🎉 working on whittling down my texture shader

@CrowhopTech
Copy link
Contributor Author

CrowhopTech commented Feb 2, 2025

@TokisanGames @Xtarsia I have trimmed down the minimal low-poly, used your colormap one out of the box, and updated the initial comment with new screenshots. I am still hacking away at the texture one but will save that for another change request. Thanks so much for your guidance!

@TokisanGames
Copy link
Owner

Did you commit it properly? The attached shader is still grey, and has uniforms that aren't used.

@CrowhopTech
Copy link
Contributor Author

CrowhopTech commented Feb 3, 2025

Weird, I'll take a look

No, I did not... derp. Fixed! There are now two files, the grayscale low-poly and the colormap version.

@TokisanGames
Copy link
Owner

TokisanGames commented Feb 3, 2025

Thanks for putting this together.

Note you pushed into the main branch. You should never do that as documented in the contributor guidelines. Normally I'm able to fix it, but somehow your repo rejected the change and github force closed this PR. I was able to merge without the PR. You were credited as a contributor. Please review all the contributor guidelines in the future.

Merged in 3f42bb2 and 3640e87.

@TokisanGames TokisanGames changed the title #422: Create minimum_lowpoly.gdshader Create minimum_lowpoly.gdshader Feb 3, 2025
@CrowhopTech
Copy link
Contributor Author

Noted! If I make contributions in the future I will do better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a lowpoly shader
4 participants