Mapping an image onto a sphere in Three.js
Asked Answered
N

3

19

I'm currently using Three.js to try and create something. I've got a sphere, and I'm trying to map the eyeball image here onto it.

The problem I have is that the result looks like this:

Enter image description here

How can I get it to map properly without looking stretched?

My code for creating the sphere and mapping the texture is below:

var geometry = new THREE.SphereGeometry(0.5,100,100);
var material = new THREE.MeshPhongMaterial( { map: THREE.ImageUtils.loadTexture('eyeballmap.jpg',THREE.SphericalRefractionMapping) } );
var eyeball = new THREE.Mesh( geometry, material );
eyeball.overdraw = true;
eyeball.castShadow = true;
scene.add( eyeball );
Narrow answered 9/2, 2014 at 19:35 Comment(0)
N
45

The key to the answer to your question is the image you linked to.

That image is similar to a MatCap image used in spherical environment mapping.

The appropriate shader for such an environment map is one that uses (a function of) the sphere's normal as the UV index into the texture map.

<script id="vertex_shader" type="x-shader/x-vertex">

varying vec3 vNormal;

void main() {

    vNormal = normal;

    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

}

</script>

<script id="fragment_shader" type="x-shader/x-fragment">

uniform sampler2D tex;

varying vec3 vNormal;

void main() {

    vec2 uv = normalize( vNormal ).xy * 0.5 + 0.5;

    vec3 color = texture2D( tex, uv ).rgb;

    if ( vNormal.z < - 0.85 ) color = vec3( 0.777, 0.74, 0.74 ); // back of eye
    
    gl_FragColor = vec4( color, 1.0 );

}

</script>
const uniforms = {
    "tex": { value: texture }   
};

const material = new THREE.ShaderMaterial( {
    uniforms        : uniforms,
    vertexShader    : document.getElementById( 'vertex_shader' ).textContent,
    fragmentShader  : document.getElementById( 'fragment_shader' ).textContent
} );

scene.add( new THREE.Mesh( new THREE.SphereGeometry( 30, 32, 16 ), material ) );

Eye Rendered on Sphere from MatCap Texture

three.js r.147

Nedranedrah answered 12/2, 2014 at 23:46 Comment(0)
C
1

The texture that you have represents only the visible part of the eye which is not the entire sphere. You need to add white space around your existing texture to allow for the part of the sphere that is not visible.

Cathey answered 9/2, 2014 at 20:20 Comment(9)
I thought there might be something wrong with the general shape of my texture - where would I put the white space? White space also (I think) wouldn't affect the distortion of the iris/pupil portion of the image, would it??Narrow
Your texture is 512x512 so you probably need to add as much white space around it (on each side) so that the pupil will take less space (as a percentage of the size of the texture). Then you will have less distortion. Another option you might look into would be to change the UV coordinates of the sphere so that your image (as is) falls at the right place.Cathey
Sorry, I'm new to this, so I don't know how much white space to add, nor do I know about what UV coordinates would be optimal.Narrow
Create a 2048x2048 texture and place yours in the center.Cathey
That doesn't work. I just get a rectangle in my sphere which contains the image.Narrow
Yes but probably there is no distortion.Cathey
If this is not what you want you need to explore the UV's option.Cathey
Create a 2048x512 image and place yours in the center :)Creight
For a 512x512 image representing half a sphere, you need a 1024x512 image where you paste your original 512x512 image in the center and leave the remaining areas (right and left) empty.Chandler
C
1

Without changing one line of code, the solution is in using a proper image, sized 1024x512 rather than 512x512:

Full equirectangular eye

But actually the original squared images represents a little more than half a sphere: half a sphere is just the circle at center, 256 pixel in radius:

Actual hemisphere

Chandler answered 17/7, 2021 at 17:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.