Best billiard ball material?

I have a lightweight browser-based billiards game and I want to improve the ball material. I think billiard balls must be the most well-known rendering challenge, but I’ve arrived at a material that seems to have limb darkening. Any suggestions?

browser demo

Current:

const material = new MeshPhysicalMaterial({
      color: color,
      roughness: 0.1,
      metalness: 0,
      clearcoat: 1.0,
      clearcoatRoughness: 0.02,
      reflectivity: 0.25,
    })

Tried to create a set of balls from scratch.

InstancedMesh of SphereGeometry and MeshPhysicalMaterial.

Material’s setup is:

      new THREE.MeshPhysicalMaterial({
        metalness: 0.5,
        roughness: 0.9,
        clearcoat: 1,
        onBeforeCompile: shader => {...}
      })

Also, there are scene.environment with intensity of 0.25, and DirectionalLight right above the “table”.

As a lifelong pool player, both look fine.

I like prisoner849’s example because the textures are more familiar and, most importantly, it is brighter. As he indicates, there is normally a fairly bright light hanging right over the pool table.

And the balls would be very smooth and hard (so they roll smoothly). Are roughness and metalness options with MeshPhysicalMaterial? I suspect the clearcoat is playing a big role in defining their appearance.

ALSO
I didn’t notice that there was a game to play. So I tried that. On the break, I sank the 15 ball in the right corner. While satisfying, that means that whoever racked up the balls did it wrong because you shouldn’t be able to sink a ball on the break with a straight shot dead center. I have seen players sink balls on the break but they always hit off-center and probably used a bit of “English” on the ball. Since pool is geometry, fixing this problem probably just involves moving the rack forward or back a bit.

I downloaded your repo locally and played with it a bit.. I wanted to add an 8ball mode but didn’t get there…

But I noticed you had:
renderer.setPixelRatio(window.devicePixelRatio * 0.5)
Which is why it looks pixelated on desktop browser…

Would be nice if you changed that to:

renderer.setPixelRatio(window.devicePixelRatio)

or even just 1 i think.
Or at least gated based on desktop vs mobile.

Regarding getting a more realistic material.. you probably want to add an enviroment map to get more lighting detail.

Hi @prisoner849 and @tailuge

I made two attempts to create balls with printed numbers.
One with a shader, with the help of Jee Peetee, and another with canvas and a direct image on the sphere, where the last two, using canvas or a direct texture on the sphere, resulted in the same finish.

I would like to learn how your wonderful spheres were made, because I haven’t been able to achieve the same result.:melting_face:

PS: The correct titles of these two pages are ball_shaders.html and ball_canvas,html.

Thanks, your render looks much nicer, I did try your material but it didn’t make much difference to mine scene - I think my black edges come from using Icosahedron (lod=4) which you can still see when close and somehow this and no subpixel resolution leave me with edges. Also your whole scene and lighting is better.

I only hack at this, I think @prisoner849 will have best advice. My texture map is just from one side. Check out full source on github/tailuge

Here is the projection

    material.onBeforeCompile = (shader: any) => {
      shader.uniforms.numberTex = { value: numberTexture }
      shader.uniforms.invScale = { value: 1 / (R * 2) }

      shader.vertexShader = shader.vertexShader.replace(
        "#include <common>",
        `#include <common>
         varying vec3 vLocalPosition;`
      )
      shader.vertexShader = shader.vertexShader.replace(
        "#include <begin_vertex>",
        `#include <begin_vertex>
         vLocalPosition = position;`
      )

      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <common>",
        `#include <common>
        uniform sampler2D numberTex;
        uniform float invScale;
        varying vec3 vLocalPosition;`
      )
      shader.fragmentShader = shader.fragmentShader.replace(
        "#include <color_fragment>",
        `#include <color_fragment>
        // Calculate the base UV mapping
        vec2 projUv = vLocalPosition.xz * invScale + 0.5;

        // Capture derivatives BEFORE the flip. 
        // This prevents the GPU from seeing the 'teleport' at the equator.
        vec2 dx = dFdx(projUv);
        vec2 dy = dFdy(projUv);

        // Flip logic for the bottom hemisphere
        if (vLocalPosition.y < 0.0) {
          projUv.x = 1.0 - projUv.x;
          // Mirror the derivatives so mipmapping stays consistent
          dx.x = -dx.x;
          dy.x = -dy.x;
        }

        projUv = clamp(projUv, 0.0, 1.0);

        // Add a negative bias to force a higher-resolution mipmap level
        // -0.5 to -1.0 usually restores the "crisp" look.
        vec4 texColor = textureGrad(numberTex, projUv, dx * 0.5, dy * 0.5);
     
        diffuseColor.rgb = texColor.rgb;`
      )
    }

@manthrax you are right, low pixel resolution hurts the look, but I want it speedy. Eightball is in the readme

play it here:

Awesommme TY! :smiley:

This is so good.

Might I respectfully suggest you disable right click context menu… with window.onContextMenu((e)=>{e.preventDefault();e.stopPropagation()}

Also, on the break, I think the ball can be placed anywhere “in the kitchen” not just on the front edge of it?

And perhaps add a URL param to set the pixelRatio to 1 :smiley: and enable antialising… and update your repo? :smiley:

and perhaps allow right click+drag for positioning the ball… ? And for dragging the ball.. maybe do a raycast on an invisible plane positioned at the ball.. to get the correct worldspace drag start/stop positions.. so the ball sticks to the cursor when being placed?

:disguised_face:

this is a very cool project.

I think it might be pretty straightforward to get this a lot more photorealistic..
Maybe slap this HDR in there…

to get this kind of look:

maybe a felt texture like this for the table: Velour Velvet Texture • Poly Haven

edit: Just found your LOD url param.. :smiley: nvm that works! :smiley:

:smiling_face_with_three_hearts:

here’s an option for the lighting also (image)…

HDRI…

billiardTable_hdri_01.hdr (5.6 MB)