Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Spiri0
Copy link
Contributor

@Spiri0 Spiri0 commented Aug 30, 2025

I have removed two console warnings because these warnings assume that you are only working with classic attribute based geometries.
My ocean geometry is a pure bufferGeometry and has no attributes. The geometry datas are calculated by a compute shader and stored in buffers. The vertex shader only receives these buffers. uv's and normals wouldn't make sense for a dynamic geometry like mine anyway.

image

For dynamic procedural geometries like mine, this is the normal way.

compute -> vertex -> fragment

Since I also use drawIndexedIndirect here, attributes are obsolete in two respects.

This doesn't mean that attributes are obsolete or useless. It's just that attribute-based geometries in WebGPU are no longer the only way to generate geometries, and they're simply no longer the best choice for landscapes and dynamic geometries or virtual geometries.

…t you are only working with classic geometries
Copy link

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 338.91
79.11
338.91
79.11
+0 B
+0 B
WebGPU 579.37
159.66
579.22
159.63
-156 B
-40 B
WebGPU Nodes 577.98
159.42
577.82
159.38
-156 B
-39 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 470.78
113.9
470.78
113.9
+0 B
+0 B
WebGPU 649.52
175.53
649.37
175.49
-156 B
-41 B
WebGPU Nodes 603.62
164.68
603.46
164.64
-156 B
-38 B

@Mugen87 Mugen87 changed the title Removed two console warnings WebGPURenderer: Removed console warnings about missing attributes. Aug 30, 2025
@sunag
Copy link
Collaborator

sunag commented Aug 30, 2025

Could you share the WGSL compiled code of your vertex and fragment shader that is giving this warning?

@Spiri0
Copy link
Contributor Author

Spiri0 commented Aug 31, 2025

Could you share the WGSL compiled code of your vertex and fragment shader that is giving this warning?

The fragment shader is very comprehensive. Perhaps the much smaller vertex shader will suffice.

const vertexShader = wgslFn(`
	fn main_vertex(
		projectionMatrix: mat4x4<f32>,
		cameraViewMatrix: mat4x4<f32>,
		modelWorldMatrix: mat4x4<f32>,
		instanceIndex: u32,
		vertexIndex: u32,
		vertexcount: u32,
		displacementBuffer: ptr<storage, array<Displacement>, read>,
		texelCoordBuffer: ptr<storage, array<TexelCoords>, read>,
	) -> vec4<f32> {

		let id = instanceIndex * vertexcount + vertexIndex;

		let vtexelCoord0 = texelCoordBuffer[ id ].texelCoord0;
		let vtexelCoord1 = texelCoordBuffer[ id ].texelCoord1;
		let vtexelCoord2 = texelCoordBuffer[ id ].texelCoord2;
		let vtexelCoord3 = texelCoordBuffer[ id ].texelCoord3;
		let lodScale0 = texelCoordBuffer[ id ].lodScale0;
		let lodScale1 = texelCoordBuffer[ id ].lodScale1;
		let lodScale2 = texelCoordBuffer[ id ].lodScale2;
		let lodScale3 = texelCoordBuffer[ id ].lodScale3;
		let morphedPosition = displacementBuffer[ id ].morphedPosition;
		let displacedPosition = displacementBuffer[ id ].displacedPosition;

		let worldPosition = modelWorldMatrix * vec4<f32>( displacedPosition.xyz, 1.0 );

		let clipPos = projectionMatrix * cameraViewMatrix * worldPosition;

		let ndc = clipPos.xyz / clipPos.w;
		var vUv = clipPos.xy / clipPos.w * 0.5 + 0.5;
		vUv = vec2<f32>( vUv.x, 1.0 - vUv.y );

		varyings.vCascadeScales = vec4<f32>( lodScale0, lodScale1, lodScale2, lodScale3 );
		varyings.vDisplacedPosition = worldPosition;
		varyings.vMorphedPosition = morphedPosition.xyz;
		varyings.vTexelCoord0 = vtexelCoord0;
		varyings.vTexelCoord1 = vtexelCoord1;
		varyings.vTexelCoord2 = vtexelCoord2;
		varyings.vTexelCoord3 = vtexelCoord3;
		varyings.vClipPos = clipPos;
		varyings.vUv = vUv;

		return clipPos;

	}

`, [ vDisplacedPosition, vMorphedPosition, vCascadeScales, vTexelCoord0, vTexelCoord1, vTexelCoord2, vTexelCoord3, vClipPos, vUv ] );

Before the vertex shader runs, a compute shader calculates the active areas and controls visibility via drawIndexedIndirect. The bottom line is that the vertex shader has no position, normal, or uv attributes. Everything comes from the two buffers.

@sunag
Copy link
Collaborator

sunag commented Aug 31, 2025

@Spiri0 Thank you, I would like to know the final output of the material (vertex+fragment), because I would like to know how the system is handling your code.

result = await renderer.debug.getShaderAsync( scene, camera, mesh );

@Spiri0
Copy link
Contributor Author

Spiri0 commented Aug 31, 2025

@Spiri0 Thank you, I would like to know the final output of the material, because I would like to know how the system is handling your code.

result = await renderer.debug.getShaderAsync( scene, camera, mesh );

That's pretty extensive. Do you wish that I post it here?
I downloaded the Mohana package from Disney that mrdoob mentioned. I would have loved to use it, but unfortunately, I have to do a lot of work with it in Blender. The corals would look very good through the water

@sunag
Copy link
Collaborator

sunag commented Aug 31, 2025

I imagine, but I would like. I would like just one material that has this problem.

@Spiri0
Copy link
Contributor Author

Spiri0 commented Aug 31, 2025

I imagine, but I would like. I would like just one material that has this problem.

{fragmentShader: '// Three.js r179 - Node System\n\n// global\ndiagnost…0 ), object.nodeUniform28 );\n\n\treturn output;\n\n}\n', vertexShader: '// Three.js r179 - Node System\n\n// directives\n\n\n//…aryings.Vertex = nodeVar0;\n\n\treturn varyings;\n\n}\n'}
fragmentShader
:
"// Three.js r179 - Node System\n\n// global\ndiag
vertexShader
:
"// Three.js r179 - Node System\n\n// directives\n\n\n// structs\n\nstruct Displacement {\n\tdisplacedPosition : vec4,\n\tmorphedPosition : vec4\n};\n\nstruct TexelCoords {\n\ttexelCoord0 : vec2,\n\ttexelCoord1 : vec2,\n\ttexelCoord2 : vec2,\n\ttexelCoord3 : vec2,\n\tlodScale0 : f32,\n\tlodScale1 : f32,\n\tlodScale2 : f32,\n\tlodScale3 : f32\n};\n\n\n// uniforms\n\nstruct NodeBuffer_1118Struct {\n\tvalue : array< u32 >\n};\n@binding( 28 ) @group( 1 )\nvar<storage, read> NodeBuffer_1118 : NodeBuffer_1118Struct;\n\nstruct NodeBuffer_1116Struct {\n\tvalue : array< Displacement >\n};\n@binding( 29 ) @group( 1 )\nvar<storage, read> NodeBuffer_1116 : NodeBuffer_1116Struct;\n\nstruct NodeBuffer_1119Struct {\n\tvalue : array< TexelCoords >\n};\n@binding( 30 ) @group( 1 )\nvar<storage, read> NodeBuffer_1119 : NodeBuffer_1119Struct;\nstruct renderStruct {\n\tcameraProjectionMatrix : mat4x4,\n\tcameraViewMatrix : mat4x4\n};\n@binding( 1 ) @group( 0 )\nvar render : renderStruct;\n\nstruct objectStruct {\n\tnodeUniform31 : mat4x4,\n\tnodeUniform35 : mat4x4,\n\tnodeUniform36 : mat4x4,\n\tnodeUniform37 : mat4x4,\n\tnodeUniform38 : mat4x4\n};\n@binding( 27 ) @group( 1 )\nvar object : objectStruct;\n\n// varyings\n\nstruct VaryingsStruct {\n\t@location( 0 ) v_positionViewDirection : vec3,\n\t@location( 3 ) v_normalViewGeometry : vec3,\n\t@location( 4 ) vMorphedPosition : vec3,\n\t@location( 5 ) vDisplacedPosition : vec4,\n\t@location( 6 ) vCascadeScales : vec4,\n\t@location( 7 ) vTexelCoord0 : vec2,\n\t@location( 8 ) vTexelCoord1 : vec2,\n\t@location( 9 ) vTexelCoord2 : vec2,\n\t@location( 10 ) vTexelCoord3 : vec2,\n\t@location( 11 ) vClipPos : vec4,\n\t@location( 12 ) vNdcGuard : vec3,\n\t@location( 13 ) vUvGuard : vec2,\n\t@location( 14 ) @interpolate( flat ) vLod : u32,\n\t@location( 15 ) vDepth : f32,\n\t@builtin( position ) Vertex : vec4\n};\nvar varyings : VaryingsStruct;\n\n// codes\nfn main_vertex ( projectionMatrix: mat4x4,\n\t\tcameraViewMatrix: mat4x4,\n\t\tmodelWorldMatrix: mat4x4,\n\t\tinstanceIndex: u32,\n\t\tvertexIndex: u32,\n\t\tvertexcount: u32,\n\t\tinstanceInfoBuffer: ptr<storage, array, read>,\n\t\tdisplacementBuffer: ptr<storage, array, read>,\n\t\ttexelCoordBuffer: ptr<storage, array, read>,\n\t\tguardCameraProjectionMatrixInverse: mat4x4,\n\t\tguardCameraProjectionMatrix: mat4x4,\n\t\tguardCameraWorldMatrix: mat4x4,\n\t\tguardCameraViewMatrix: mat4x4, ) -> vec4 {\n\n\t\tlet id = instanceIndex * vertexcount + vertexIndex;\n\n\t\tlet vtexelCoord0 = texelCoordBuffer[ id ].texelCoord0;\n\t\tlet vtexelCoord1 = texelCoordBuffer[ id ].texelCoord1;\n\t\tlet vtexelCoord2 = texelCoordBuffer[ id ].texelCoord2;\n\t\tlet vtexelCoord3 = texelCoordBuffer[ id ].texelCoord3;\n\t\tlet lodScale0 = texelCoordBuffer[ id ].lodScale0;\n\t\tlet lodScale1 = texelCoordBuffer[ id ].lodScale1;\n\t\tlet lodScale2 = texelCoordBuffer[ id ].lodScale2;\n\t\tlet lodScale3 = texelCoordBuffer[ id ].lodScale3;\n\t\tlet morphedPosition = displacementBuffer[ id ].morphedPosition;\n\t\tlet displacedPosition = displacementBuffer[ id ].displacedPosition;\n\n\t\tlet clipPos = projectionMatrix * cameraViewMatrix * modelWorldMatrix * vec4( displacedPosition.xyz, 1.0 );\n\t\tlet clipPosGuard = guardCameraProjectionMatrix * guardCameraViewMatrix * modelWorldMatrix * vec4( displacedPosition.xyz, 1.0 );\n\n\t\tlet ndc = clipPos.xyz / clipPos.w;\n\t\tlet ndcGuard = clipPosGuard.xyz / clipPosGuard.w;\n\n\t\tvar vUv = clipPos.xy / clipPos.w * 0.5 + 0.5;\n\t\tvUv = vec2( vUv.x, 1.0 - vUv.y );\n\n\t\tvar vUvGuard = clipPosGuard.xy / clipPosGuard.w * 0.5 + 0.5;\n\t\tvUvGuard = vec2( vUvGuard.x, 1.0 - vUvGuard.y );\n\n\t\tvaryings.vDepth = clipPos.w;\n\t\tvaryings.vCascadeScales = vec4( lodScale0, lodScale1, lodScale2, lodScale3 );\n\t\tvaryings.vDisplacedPosition = modelWorldMatrix * vec4( displacedPosition.xyz, 1.0 );\n\t\tvaryings.vMorphedPosition = morphedPosition.xyz;\n\t\tvaryings.vTexelCoord0 = vtexelCoord0;\n\t\tvaryings.vTexelCoord1 = vtexelCoord1;\n\t\tvaryings.vTexelCoord2 = vtexelCoord2;\n\t\tvaryings.vTexelCoord3 = vtexelCoord3;\n\t\tvaryings.vLod = u32( morphedPosition.w );\n\t\tvaryings.vNdcGuard = ndcGuard;\n\t\tvaryings.vUvGuard = vUvGuard;\n\t\tvaryings.vClipPos = clipPosGuard;\n \n\n\t\treturn clipPos;\n\n\t}\n\n\n\n@vertex\nfn main( @Builtin( instance_index ) instanceIndex : u32,\n\t@builtin( vertex_index ) vertexIndex : u32 ) -> VaryingsStruct {\n\n\t// vars\n\t\n\tvar nodeVar0 : vec4;\n\tvar v_positionView : vec3;\n\tvar positionLocal : vec3;\n\n\n\t// flow\n\t// code\n\n\tnodeVar0 = main_vertex( render.cameraProjectionMatrix, render.cameraViewMatrix, object.nodeUniform31, instanceIndex, vertexIndex, 625u, &NodeBuffer_1118.value, &NodeBuffer_1116.value, &NodeBuffer_1119.value, object.nodeUniform35, object.nodeUniform36, object.nodeUniform37, object.nodeUniform38 );\n\n\t// result\n\n\tvaryings.Vertex = nodeVar0;\n\n\treturn varyings;\n\n}\n"
[[Prototype]]
:
...

image

I took the uv warning into account at the same time, because in my virtual geometry project, where I don't have any attributes, I kept getting the warning that the position attribute was missing.

The WebGPU messages in the console are fine. Before I start threejs, I first create a simple adapter to read the GPU limits from it. I then immediately delete it before starting threejs with extended limits.

@sunag
Copy link
Collaborator

sunag commented Aug 31, 2025

vec4,\n\tmorphedPosition : vec4\n};\n\nstruct TexelCoords {\n\ttexelCoord0 : vec2,\n\ttexelCoord1 : vec2,\n\ttexelCoord2 : vec2,\n\ttexelCoord3 : vec2,\n\tlodScale0 : f32,\n\tlodScale1 : f32,\n\tlodScale2 : f32,\n\tlodScale3 : f32\n};\n\n\n// uniforms\n\nstruct NodeBuffer_1118Struct {\n\tvalue : array< u32 >\n};\n@binding( 28 ) @group( 1 )\nvar<storage, read> NodeBuffer_1118 : NodeBuffer_1118Struct;\n\nstruct NodeBuffer_1116Struct {\n\tvalue : array< Displacement >\n};\n@binding( 29 ) @group( 1 )\nvar<storage, rea

Could you post in code format?

@Spiri0
Copy link
Contributor Author

Spiri0 commented Aug 31, 2025

Could you post in code format?

This is what I got with the debug command you mentioned:

const result = await this.params_.renderer.debug.getShaderAsync( this.params_.scene, this.params_.camera, this.mesh );
console.log( result );

I would have to catch this in the WGSLNodeBuilder

@sunag
Copy link
Collaborator

sunag commented Aug 31, 2025

This is what I got with the debug command you mentioned:
I would have to catch this in the WGSLNodeBuilder

Yeah, I thought I had described that I needed the code #31788 (comment), with this function you can get the result of the compiled code, without having to hack or debug the WGSLNodeBuilder. But the process is up to you, unfortunately the way it was sent it is not possible for me to understand what the system is doing.

@Spiri0
Copy link
Contributor Author

Spiri0 commented Aug 31, 2025

Let's see if I can significantly reduce it. Otherwise, there's so much in there. I'll try to narrow it down later.

@Spiri0
Copy link
Contributor Author

Spiri0 commented Sep 1, 2025

I've now intercepted the fragment shader in WGSLNodeBuilder. I've also simplified the fragment shader considerably. I still see the warning with this much lighter fragment shader. I hope this helps.

// Three.js r179 - Node System

// global
diagnostic( off, derivative_uniformity );


// structs

struct OutputStruct {
	@location(0) color: vec4<f32>
};
var<private> output : OutputStruct;

// uniforms
@binding( 0 ) @group( 1 ) var nodeUniform0_sampler : sampler;
@binding( 1 ) @group( 1 ) var nodeUniform0 : texture_2d_array<f32>;
@binding( 2 ) @group( 1 ) var nodeUniform1_sampler : sampler;
@binding( 3 ) @group( 1 ) var nodeUniform1 : texture_2d_array<f32>;
@binding( 4 ) @group( 1 ) var nodeUniform2_sampler : sampler;
@binding( 5 ) @group( 1 ) var nodeUniform2 : texture_2d_array<f32>;
@binding( 6 ) @group( 1 ) var nodeUniform3_sampler : sampler;
@binding( 7 ) @group( 1 ) var nodeUniform3 : texture_2d_array<f32>;

// codes
fn main_fragment ( ifft_texture0: texture_2d_array<f32>,
        ifft_texture1: texture_2d_array<f32>,
        ifft_texture2: texture_2d_array<f32>,
        ifft_texture3: texture_2d_array<f32>,
        ifft_sampler0: sampler,
        ifft_sampler1: sampler,
        ifft_sampler2: sampler,
        ifft_sampler3: sampler,

        vCascadeScales: vec4<f32>,
        vTexelCoord0: vec2<f32>,
        vTexelCoord1: vec2<f32>,
        vTexelCoord2: vec2<f32>,
        vTexelCoord3: vec2<f32>, ) -> vec4<f32> {

        var Normal_0: vec4<f32> = textureSample( ifft_texture0, ifft_sampler0, vTexelCoord0, 1u ) * vCascadeScales.x;
        var Normal_1: vec4<f32> = textureSample( ifft_texture1, ifft_sampler1, vTexelCoord1, 1u ) * vCascadeScales.y;
        var Normal_2: vec4<f32> = textureSample( ifft_texture2, ifft_sampler2, vTexelCoord2, 1u ) * vCascadeScales.z;
        var Normal_3: vec4<f32> = textureSample( ifft_texture3, ifft_sampler3, vTexelCoord3, 1u ) * vCascadeScales.w;

        var derivatives: vec4<f32> = normalize( Normal_0 + Normal_1 + Normal_2 + Normal_3 );
        var slope: vec2<f32> = vec2<f32>(derivatives.x / (1.0 + derivatives.z), derivatives.y / (1.0 + derivatives.w));
        var N: vec3<f32> = normalize(vec3(-slope.x, 1.0, -slope.y));

        if ( !( all( isFinite( N ) ) ) ) {

            N = vec3<f32>( 0.0, 1.0, 0.0 );

        }

        return vec4<f32>( N, 1.0 );

    }

    fn isFinite( v: vec3<f32> ) -> bool {
        return all(
            vec3<bool>(
                v.x == v.x && abs( v.x ) < 1e30,
                v.y == v.y && abs( v.y ) < 1e30,
                v.z == v.z && abs( v.z ) < 1e30
            )
        );
    }



@fragment
fn main( @location( 0 ) v_positionViewDirection : vec3<f32>,
	@location( 3 ) v_normalViewGeometry : vec3<f32>,
	@location( 4 ) vCascadeScales : vec4<f32>,
	@location( 5 ) vTexelCoord0 : vec2<f32>,
	@location( 6 ) vTexelCoord1 : vec2<f32>,
	@location( 7 ) vTexelCoord2 : vec2<f32>,
	@location( 8 ) vTexelCoord3 : vec2<f32>,
	@location( 9 ) vDisplacedPosition : vec4<f32>,
	@location( 10 ) vMorphedPosition : vec3<f32>,
	@location( 11 ) @interpolate( flat ) vLod : u32,
	@location( 12 ) vUvGuard : vec2<f32>,
	@location( 13 ) vNdcGuard : vec3<f32>,
	@location( 14 ) vDepth : f32,
	@location( 15 ) vClipPos : vec4<f32> ) -> OutputStruct {

	// vars
	



	// flow
	// code


	// result

	output.color = main_fragment( nodeUniform0, nodeUniform1, nodeUniform2, nodeUniform3, nodeUniform0_sampler, nodeUniform1_sampler, nodeUniform2_sampler, nodeUniform3_sampler, vCascadeScales, vTexelCoord0, vTexelCoord1, vTexelCoord2, vTexelCoord3 );

	return output;

}

These two weren't mine. They were inserted by the WGSLNodeBuilder. Could this be the cause of the warning?

v_positionViewDirection : vec3,
v_normalViewGeometry : vec3,

@sunag
Copy link
Collaborator

sunag commented Sep 1, 2025

These two weren't mine. They were inserted by the WGSLNodeBuilder. Could this be the cause of the warning?

Yes, I suspected this might happen, so I'm in favor of polishing the approach or the system rather than removing the warnings. But I need to know the root cause of the problem.

In this regard, I'll need more details on how the material was setup and post-processing if necessary. If you could provide some minimalist code, it would be very helpful.

Thank you for simplifying this example.

@Spiri0
Copy link
Contributor Author

Spiri0 commented Sep 2, 2025

In this regard, I'll need more details on how the material was setup and post-processing if necessary. If you could provide some minimalist code, it would be very helpful.

Thank you for simplifying this example.

I'll try to get to it today. If not tomorrow. I've disabled post-processing. But I will write to you.

@Spiri0
Copy link
Contributor Author

Spiri0 commented Sep 3, 2025

Here, I have the same phenomenon with the position. Why there's a position warning but no normals warning is beyond me, since I don't have any normals attributes here, just like with the ocean.

image

I'm trying to reproduce at least one of these warnings with this codepen, as it works similarly. Only there are no warnings. The main difference is that I use drawIndexedIndirect in my apps, but I can't yet tell if that might have something to do with it.

https://codepen.io/Spiri0/pen/zxxvqYb?editors=0010

I'll try to reproduce this with a simple example.

@Spiri0
Copy link
Contributor Author

Spiri0 commented Sep 4, 2025

OK, I got it. The cubeTexture node is triggering the warning. The other warnings are not of interest here, just the normals warning which is now triggered if I include the cubeTexture node in the shader params.

image

I'm using the WebGLCubeRenderTarget instead of the PMREMGenerator. There was something there, I'll have to look into it again, but for now it only delivers the texture. Since the sampler node doesn't trigger this warning, I strongly suspect the cubeTexture node as the issue for this. I'll also take a closer look at the position warning, because I'm not using a cubeTexture node there. It must be something else.

const fragmentShaderParams = {
    vColor: vColor,
    envTexture: cubeTexture( cubeRenderTarget.texture ),      //<------ triggers the normal warning
    envSampler: sampler( cubeRenderTarget.texture ),
};

comment this out and the warning disappears

envTexture: cubeTexture( cubeRenderTarget.texture ), 

@sunag
Copy link
Collaborator

sunag commented Sep 5, 2025

envTexture: cubeTexture( cubeRenderTarget.texture ), //<------ triggers the normal warning

This happened because if you don’t use a parameter like uv in cubeTexture( texture, uv = null ) it will use the texture’s uv reference as texture.mapping, which in this case is CubeReflectionMapping. It will then use the reflectVector node, which relies on a normalView node in the process.

You can solve this by adding a custom uv like cubeTexture( cubeRenderTarget.texture, vec3() ).

@Spiri0
Copy link
Contributor Author

Spiri0 commented Sep 5, 2025

envTexture: cubeTexture( cubeRenderTarget.texture ), //<------ triggers the normal warning

This happened because if you don’t use a parameter like uv in cubeTexture( texture, uv = null ) it will use the texture’s uv reference as texture.mapping, which in this case is CubeReflectionMapping. It will then use the reflectVector node, which relies on a normalView node in the process.

You can solve this by adding a custom uv like cubeTexture( cubeRenderTarget.texture, vec3() ).

Then I must apologize for my lack of knowlage. Sorry for the PR. I'll figure out what triggers the position warning.

@sunag
Copy link
Collaborator

sunag commented Sep 5, 2025

The PR is important because of the conclusion, analyzing again you are not using UV. It is something we can improve

@Spiri0
Copy link
Contributor Author

Spiri0 commented Sep 5, 2025

The PR is important because of the conclusion, analyzing again you are not using UV. It is something we can improve

Thank you, then I'm relieved.
I can't pass a uv because I have to create it in the shader itself due to the dynamic surface and the viewDir.

var R = reflect( -viewDir, N );

var halfVec = ( normalize( viewDir + N ) );

R = halfVec;
R = vec3<f32>(R.y, R.x, R.z);
R.z *= -1;   //Necessary for cubetexture to be interpreted correctly. Perhaps a similar problem to the one with PMREM, where the page assignment wasn't correct.

var texcoord = vec3<f32>( R.x, R.y, R.z );

I might have the opportunity to present the power of Threejs at the university in Heilbronn. They've been teaching there in Unity so far. I was there in June, and they really liked the Ocean repo. Since then, however, I've implemented it much more efficiently and resource-friendly, without render hickups. Because I'm currently working intensively on further developing realistic water with transparency and refraction, I haven't had the time to continue working on dispatchWorkgroupsIndirect yet. The extension already works fine, but I haven't been able to continue with the count topic since then.
Unfortunately, all the corals from the Mohana package are textureless in Blender, so I'll have to painstakingly rework them. But they'll definitely look great through the water.

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.

3 participants