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

Latest three.js #8

Open
andrewvarga opened this issue Feb 21, 2024 · 14 comments
Open

Latest three.js #8

andrewvarga opened this issue Feb 21, 2024 · 14 comments

Comments

@andrewvarga
Copy link

Hi! This is a great library!

I'm having a bit of trouble using it with the latest three.js, is that something you're planning to add support for?
thanks!

@lucas-jones
Copy link
Owner

Sure, I've upgraded this project to 161

Commit
0df3df7

@andrewvarga
Copy link
Author

Thank you for the quick answer!

For me the vertex shader was not compiling:

ERROR: 0:86: 'uv2' : redefinition

but if I removed the attribute vec2 uv2; line it compiled. I wonder why it is different for you vs me?

I'm trying to integrate this into three.js editor, and the result with the Avocado sample model is something like this after I apply the lightmap (and set channel to 2 as in your code):
Screenshot 2024-02-22 at 14 49 25

It looks like it's using the wrong UV channel?

@lucas-jones
Copy link
Owner

lucas-jones commented Feb 22, 2024

You might need to set the textures channel to 2 (not quite sure how in Threejs Editor)

I had to change this in code too
0df3df7#diff-dba681e72df960f9fff3b45c7a5700c9a7b4bf9e9d218ffacb910cf0bb454556R296

Also the lightmapping process will generate & build UV2. You might need to ensure these are added to the model

How did you export the lightmap?

@andrewvarga
Copy link
Author

andrewvarga commented Mar 1, 2024

You might need to set the textures channel to 2 (not quite sure how in Threejs Editor)

I had to change this in code too 0df3df7#diff-dba681e72df960f9fff3b45c7a5700c9a7b4bf9e9d218ffacb910cf0bb454556R296

Also the lightmapping process will generate & build UV2. You might need to ensure these are added to the model

How did you export the lightmap?

Sorry it took some time to test a few things out..

if all I do is clone your repo and try the demo with the official Avocado.glb I get this error:
[.WebGL-0x12400c26900] GL_INVALID_OPERATION: Vertex buffer is not big enough for the draw call
https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/Avocado/glTF-Binary

I found this is because this model has a tangent attribute which is not handled by xatlas-three probably, so adding this code helped after the packAtlas call:

await unwrapper.packAtlas(geometry, 'uv2', 'uv');
// fix for tangents
for (const g of geometry) {
   if (g.hasAttribute('tangent')) {
      g.deleteAttribute('tangent');
      g.computeTangents();
   }
}

Lightmap export: I just added a function that reads the texture into a canvas and displays it in the dom:

function debugTexture(renderer, renderTarget) {
	const width = renderTarget.width;
	const height = renderTarget.height;
	const pixels = new Float32Array(width * height * 4); // 4 components (RGBA) per pixel, as floats
	renderer.readRenderTargetPixels(renderTarget, 0, 0, width, height, pixels);

	// Step 3: Convert the floating point data to 8-bit data for visualization
	const displayPixels = new Uint8Array(width * height * 4);
	for (let i = 0; i < pixels.length; i++) {
		// Assuming the float data is in the range [0, 1], scale to [0, 255]
		// Adjust this as necessary based on how your data is scaled
		displayPixels[i] = Math.min(255, Math.max(0, Math.floor(255 * pixels[i])));
	}

	// Continue as before to create a data texture, canvas, and image
	const dataTexture = new THREE.DataTexture(displayPixels, width, height, THREE.RGBAFormat);
	dataTexture.needsUpdate = true;

	const canvas = document.createElement('canvas');
	canvas.width = width;
	canvas.height = height;
	const context = canvas.getContext('2d');

	const imgData = context.createImageData(width, height);
	imgData.data.set(displayPixels);
	context.putImageData(imgData, 0, 0);

	debugImage = debugImage || new Image();
	debugImage.src = canvas.toDataURL();
	debugImage.style.zIndex = 999999;
	debugImage.style.top = '300px';
	debugImage.style.position = 'absolute';

	if (!debugImage.parentNode) {
		document.body.appendChild(debugImage);
	}
}

@andrewvarga
Copy link
Author

The channel seems to be correct now, there are a few seaming artifacts on the lightmap, I wonder if those can be improved somehow?
Screenshot 2024-03-01 at 16 31 49

@lucas-jones
Copy link
Owner

This will be due to light leaking - the lightmapper trying to sample points that are placed inside the model rather than the surface. Good article on it here

You could render at a higher resolution and then downscale the lightmap manually - might help remove the artifacts.

This repo is more of a proof of concept than an actual tool

If you're after a proper tool

  • You can bake Indirect Light / Ambient Occlusion using Blender
  • There is also a Addon for Blender for lightmap baking - The Lightmapper

@andrewvarga
Copy link
Author

  • The Lightmapper

Thank you that is useful! Do you think there are any parameters I could tweak that could help this either in lightmapperOptions or at the unwrap stage?
xatlas-three has a lot of parameters, but I couldn't find one that resolved this for me..

unwrapper.chartOptions = {
   fixWinding: true,
   maxBoundaryLength: 0,
   maxChartArea: 0,
   maxCost: 2,
   maxIterations: 1,
   normalDeviationWeight: 2,
   normalSeamWeight: 4,
   roundnessWeight: 0.009999999776482582,
   straightnessWeight: 6,
   textureSeamWeight: 0.1,
   useInputMeshUvs: false,
};
unwrapper.packOptions = {
   bilinear: true,
   blockAlign: false,
   bruteForce: false,
   createImage: false,
   maxChartSize: 0,
   padding: 0,
   resolution: 1024,
   rotateCharts: true,
   rotateChartsToAxis: true,
   texelsPerUnit: 0
};

In my particular case I'm mostly interested in the ambient occlusion part, and I think the black lines are coming from the texels which are "in between" the faces of the unwrapped geometry, so I wonder if it would be possible that the "lightmap" (more like an occlusion map in this case) defaults to being white and becomes darker when there are more hits by bvhIntersectFirstHit..maybe somehow detecting when the current pixel doesn't belong to any unwrapped triangle in the geometry and make that white..?

@gkjohnson
Copy link
Contributor

there are a few seaming artifacts on the lightmap, I wonder if those can be improved somehow?

These are caused by the color transition at the edge of the UV islands causing the sample to transition to black when sampling due to linear interpolation. There are no UV modifications that can be made to address this and simply changing the background color will just change the color of the seam.

Instead the best approach is to inflate the color of the UV islands into the uncolored background area so there are no interpolation artifacts at the edges. Of course these things still break down with mipmapping (regular texturing does, as well) but in typical viewing scenarios it should be okay. You can read more about it in this Edge Padding Polygon article. As far as I know there's no out-of-the-box solution to generating this in the three.js ecosystem.

@lucas-jones
Copy link
Owner

Nice article on edge padding

Some dilation is done when rendering the texture atlas (world positions & normals) - in the hope it would pad out a few pixels to prevents this

https://github.com/lucas-jones/three-lightmap-baker/blob/main/src/atlas/renderAtlas.ts#L125-L131

@gkjohnson
Copy link
Contributor

Some dilation is done when rendering the texture atlas (world positions & normals) - in the hope it would pad out a few pixels

It looks like that offset value shifts every uv point consistently (ie everything will be shifted down to the right, for example), which would likely exacerbate the problem. Even if you expanded the UV islands "outward" so an apparent padding was added this would still cause issues since the positions used to sample light will no longer be aligned (at least slightly) to the same positions used when sampling the texture.

I think the best solution is a post-processing of the generated light map to flood the background with data / color from the rendered uv islands.

@andrewvarga
Copy link
Author

there are a few seaming artifacts on the lightmap, I wonder if those can be improved somehow?

These are caused by the color transition at the edge of the UV islands causing the sample to transition to black when sampling due to linear interpolation. There are no UV modifications that can be made to address this and simply changing the background color will just change the color of the seam.

Instead the best approach is to inflate the color of the UV islands into the uncolored background area so there are no interpolation artifacts at the edges. Of course these things still break down with mipmapping (regular texturing does, as well) but in typical viewing scenarios it should be okay. You can read more about it in this Edge Padding Polygon article. As far as I know there's no out-of-the-box solution to generating this in the three.js ecosystem.

Thanks! Does that mean that if I set the textures filtering to NEAREST, those artifacts should be gone (of course the lightmap will become pixelated which is a bigger problem, so this is not a solution, I just want to verify that this is the problem).

Maybe I don't understand this in all detail, but are there any pixels in the position/normal texture that don't belong to any geometry in the unwrapped UV set? So these would be pixels that would not be covered by any triangles if you draw the geometry to this texture in UV space. I was just thinking if there are such pixels, it would be an easy way to have those texels white instead of black..

As a quick post processing hack, I also tried tried to just make fully black pixels -> fully white. Note t
So from this (where you can clearly see some areas of the texture that don't belong to any triangles on the mesh, eg. the bottom left corner):
occlusion

To this:
occlusion_white

Note that I'm using this texture as an occlusion map, not a lightmap (even though technically it's a lightmap, I only use the ambient light.
Surprisingly this removed those small black line artifacts and there's nothing wrong that I see..I'm thinking it's because the area of the mesh which would be fully black isn't really visible in 3D in this particular model..

@gkjohnson
Copy link
Contributor

Thanks! Does that mean that if I set the textures filtering to NEAREST, those artifacts should be gone

No, it does not. Textures are limited in resolution so pixels do not exactly align with the edges of triangles.

are there any pixels in the position/normal texture that don't belong to any geometry in the unwrapped UV set

Of course, these are the black pixels outside of the UV islands.

I was just thinking if there are such pixels, it would be an easy way to have those texels white instead of black..

You're just shifting the problem. The seams will be now be white instead of black. It may not be as noticeable in the avocado case but any seams in darker corners will now just be white. If you want this to work in the general case you need to inflate the color from the islands out to the edges of texture as the link I shared demonstrates.

@andrewvarga
Copy link
Author

Thank you, that makes sense, I'll give color inflation a try!

I have tried the lightmapper with multiple models and have a few problems with models like this:
Screenshot 2024-03-28 at 16 40 48

this is the generated lightmap:
occlusion (1)

This is the original model and the generated glb (lightmap added to occlusion map
sofa.zip

Do you have any ideas what this could be caused by? Maybe problems in the original mesh geometry / topology?
It seems on the lightmap there are some diagonal "stretches" that seemingly overlap large areas covering multiple islands..

@lucas-jones
Copy link
Owner

The model looks fine before the exports. Looks like the buffer attributes are messed up somehow (index buffer might be wrong?)

generateAtlas will run unwrapper.PackAtlas REF

Which then uses the xatlas-three library to convert the geometry into ones that support UV2 (can change the attributes from the original) REF

Or it could even be with the way your exporting the GLB

@lucas-jones lucas-jones reopened this Mar 28, 2024
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

No branches or pull requests

3 participants