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

fix: Fix screenCoordinates outline rendering in WebGL #1492

Merged
merged 3 commits into from
Sep 24, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion packages/three-vrm-materials-mtoon/examples/feature-test.html
Original file line number Diff line number Diff line change
@@ -270,11 +270,57 @@

},

// outline world
{
map: textureUVGrid,
outlineWidthMode: 'worldCoordinates',
outlineWidthFactor: 0.05,
outlineColorFactor: 0x00ff00,
},

// outline world, masked, unlit
{
map: textureUVGrid,
outlineWidthMode: 'worldCoordinates',
outlineWidthFactor: 0.05,
outlineColorFactor: 0x00ff00,
outlineWidthMultiplyTexture: textureBinaryHalf,
outlineLightingMixFactor: 0.0,
},

// outline screen
{
map: textureUVGrid,
outlineWidthMode: 'screenCoordinates',
outlineWidthFactor: 0.05,
outlineColorFactor: 0xff0000,
},

].map( ( params, i ) => {

const material = new MToonMaterial( params );
const mesh = new THREE.Mesh( geometrySphere, material );

// if outline is enabled we need to duplicate the material and assign it to the mesh
if ( material.outlineWidthMode !== 'none' ) {

// duplicate the material for outline use
const materialOutline = mesh.material.clone();
materialOutline.isOutline = true;
materialOutline.side = THREE.BackSide;

mesh.material = [ mesh.material, materialOutline ];

// make two geometry groups out of a same buffer
const geometry = mesh.geometry; // mesh.geometry is guaranteed to be a BufferGeometry in GLTFLoader
const primitiveVertices = geometry.index ? geometry.index.count : geometry.attributes.position.count / 3;
geometry.addGroup( 0, primitiveVertices, 0 );
geometry.addGroup( 0, primitiveVertices, 1 );

}

console.log( mesh.material );

const x = ( i % 5 ) - 2.0;
const y = Math.floor( i / 5 ) - 2.0;
mesh.position.set( x, y, 0 );
@@ -309,7 +355,20 @@

for ( const mesh of meshes ) {

mesh.material.update( delta );
// mesh.material can be either array or single material
if ( Array.isArray( mesh.material ) ) {

for ( const material of mesh.material ) {

material.update( delta );

}

} else {

mesh.material.update( delta );

}

}

Original file line number Diff line number Diff line change
@@ -273,11 +273,55 @@

},

// outline world
{
map: textureUVGrid,
outlineWidthMode: 'worldCoordinates',
outlineWidthFactor: 0.05,
outlineColorFactor: 0x00ff00,
},

// outline world, masked, unlit
{
map: textureUVGrid,
outlineWidthMode: 'worldCoordinates',
outlineWidthFactor: 0.05,
outlineColorFactor: 0x00ff00,
outlineWidthMultiplyTexture: textureBinaryHalf,
outlineLightingMixFactor: 0.0,
},

// outline screen
{
map: textureUVGrid,
outlineWidthMode: 'screenCoordinates',
outlineWidthFactor: 0.05,
outlineColorFactor: 0xff0000,
},

].map( ( params, i ) => {

const material = new MToonNodeMaterial( params );
const mesh = new THREE.Mesh( geometrySphere, material );

// if outline is enabled we need to duplicate the material and assign it to the mesh
if ( params.outlineWidthMode !== 'none' ) {

// duplicate the material for outline use
const materialOutline = mesh.material.clone();
materialOutline.isOutline = true;
materialOutline.side = THREE.BackSide;

mesh.material = [ mesh.material, materialOutline ];

// make two geometry groups out of a same buffer
const geometry = mesh.geometry; // mesh.geometry is guaranteed to be a BufferGeometry in GLTFLoader
const primitiveVertices = geometry.index ? geometry.index.count : geometry.attributes.position.count / 3;
geometry.addGroup( 0, primitiveVertices, 0 );
geometry.addGroup( 0, primitiveVertices, 1 );

}

const x = ( i % 5 ) - 2.0;
const y = Math.floor( i / 5 ) - 2.0;
mesh.position.set( x, y, 0 );
@@ -312,7 +356,20 @@

for ( const mesh of meshes ) {

mesh.material.update( delta );
// mesh.material can be either array or single material
if ( Array.isArray( mesh.material ) ) {

for ( const material of mesh.material ) {

material.update( delta );

}

} else {

mesh.material.update( delta );

}

}

1 change: 0 additions & 1 deletion packages/three-vrm-materials-mtoon/src/MToonMaterial.ts
Original file line number Diff line number Diff line change
@@ -606,7 +606,6 @@ export class MToonMaterial extends THREE.ShaderMaterial {
DEBUG_NORMAL: this._debugMode === 'normal',
DEBUG_LITSHADERATE: this._debugMode === 'litShadeRate',
DEBUG_UV: this._debugMode === 'uv',
OUTLINE_WIDTH_WORLD: this._isOutline && this._outlineWidthMode === MToonMaterialOutlineWidthMode.WorldCoordinates,
OUTLINE_WIDTH_SCREEN:
this._isOutline && this._outlineWidthMode === MToonMaterialOutlineWidthMode.ScreenCoordinates,
};
21 changes: 8 additions & 13 deletions packages/three-vrm-materials-mtoon/src/shaders/mtoon.vert
Original file line number Diff line number Diff line change
@@ -92,27 +92,22 @@ void main() {

vViewPosition = - mvPosition.xyz;

float outlineTex = 1.0;

#ifdef OUTLINE
float worldNormalLength = length( transformedNormal );
vec3 outlineOffset = outlineWidthFactor * worldNormalLength * objectNormal;

#ifdef USE_OUTLINEWIDTHMULTIPLYTEXTURE
vec2 outlineWidthMultiplyTextureUv = ( outlineWidthMultiplyTextureUvTransform * vec3( vUv, 1 ) ).xy;
outlineTex = texture2D( outlineWidthMultiplyTexture, outlineWidthMultiplyTextureUv ).g;
#endif

#ifdef OUTLINE_WIDTH_WORLD
Copy link
Contributor

@yue4u yue4u Sep 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so OUTLINE_WIDTH_WORLD is no longer needed?

Copy link
Contributor Author

@0b5vr 0b5vr Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, while I agree that it is too implicit to acknowledge that OUTLINE && !OUTLINE_WIDTH_SCREEN == OUTLINE_WIDTH_WORLD.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up removing the line @ 1426a61

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe leave a comment or something in shader code would help future us? Anyway LGTM.

float worldNormalLength = length( transformedNormal );
vec3 outlineOffset = outlineWidthFactor * outlineTex * worldNormalLength * objectNormal;
gl_Position = projectionMatrix * modelViewMatrix * vec4( outlineOffset + transformed, 1.0 );
float outlineTex = texture2D( outlineWidthMultiplyTexture, outlineWidthMultiplyTextureUv ).g;
outlineOffset *= outlineTex;
#endif

#ifdef OUTLINE_WIDTH_SCREEN
vec3 clipNormal = ( projectionMatrix * modelViewMatrix * vec4( objectNormal, 0.0 ) ).xyz;
vec2 projectedNormal = normalize( clipNormal.xy );
projectedNormal.x *= projectionMatrix[ 0 ].x / projectionMatrix[ 1 ].y;
gl_Position.xy += 2.0 * outlineWidthFactor * outlineTex * projectedNormal.xy;
outlineOffset *= vViewPosition.z / projectionMatrix[ 1 ].y;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outlineOffset has been fixed and using the same logic in tsl

#endif

gl_Position = projectionMatrix * modelViewMatrix * vec4( outlineOffset + transformed, 1.0 );

gl_Position.z += 1E-6 * gl_Position.w; // anti-artifact magic
#endif