Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lithophane effect in Three JS

Is there a way to get Lithophane effect using three.js

Sample image.

Currently i have tried different materials with transparency and opacity but have not succeeded.

    <html lang="en">
<head>
    <title>Lith (Three.js)</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

</head>
<body>

<script src="js/three.min.js"></script>
<script src="./js/dat.gui.min.js"></script>
<script src="./js/STLLoader.js"></script>
<script src="js/Detector.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/SkyShader.js"></script>
<script src="js/THREEx.WindowResize.js"></script>

<div id="ThreeJS" style="position: absolute; left:0px; top:0px"></div>
<script>
var container, scene, camera, renderer, controls, stats;
var clock = new THREE.Clock();
var cube;

init();
animate();

function init() 
{
    // SCENE
    scene = new THREE.Scene();
    // CAMERA
    var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight;
    var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000;
    camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
    scene.add(camera);
    camera.position.set(0,150,400);
    camera.lookAt(scene.position);  
    // RENDERER
    if ( Detector.webgl )
        renderer = new THREE.WebGLRenderer( {antialias:true} );
    else
        renderer = new THREE.CanvasRenderer(); 
    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    renderer.setClearColor( 0x999999 );
    container = document.getElementById( 'ThreeJS' );
    container.appendChild( renderer.domElement );
    // EVENTS
    THREEx.WindowResize(renderer, camera);

    controls = new THREE.OrbitControls( camera, renderer.domElement );

    // SKYBOX/FOG
    var skyBoxGeometry = new THREE.CubeGeometry( 10000, 10000, 10000 );
    var skyBoxMaterial = new THREE.MeshBasicMaterial( { color: 0x9999ff, side: THREE.BackSide } );
    var skyBox = new THREE.Mesh( skyBoxGeometry, skyBoxMaterial );
    // scene.add(skyBox);
    scene.fog = new THREE.FogExp2( 0x9999ff, 0.00025 );

    ////////////
    // CUSTOM //
    ////////////

    // must enable shadows on the renderer 
    renderer.shadowMapEnabled = true;

    // "shadow cameras" show the light source and direction

    // spotlight #1 -- yellow, dark shadow
    var spotlight = new THREE.SpotLight(0xffff00);
    spotlight.position.set(0,150,-50);
    spotlight.shadowCameraVisible = true;
    spotlight.shadowDarkness = 0.8;
    spotlight.intensity = 2;
    // must enable shadow casting ability for the light
    spotlight.castShadow = true;
    scene.add(spotlight);

    var sphereSize = 10;
    var pointLightHelper = new THREE.SpotLightHelper( spotlight, sphereSize );
    scene.add( pointLightHelper );


    var light = new THREE.SpotLight(0x999999);
    light.intensity = 0.6;
    camera.add(light);

    var loader = new THREE.STLLoader();
    loader.load('./TestOriginal.stl', function(object) {
        meshObject = object;
        var color = new THREE.Color( 0xffffff );
        var material = new THREE.MeshPhongMaterial({
                    color: color,//'white',
                    side: THREE.DoubleSide,
                    //shading: THREE.SmoothShading, 
                    opacity: 0.6, 
                    transparent: true
                });
        this.mesh = new THREE.Mesh(object, material);

        mesh.position.set(0,0,0);
        scene.add(mesh);

        mesh.position.set(0,0,0);
        var newScale = 1;
        mesh.geometry.computeBoundingBox();
        boundingBox = mesh.geometry.boundingBox;
        mesh.translateX(-((boundingBox.max.x + boundingBox.min.x) * newScale) / 2);
        mesh.translateY(-((boundingBox.max.y + boundingBox.min.y) * newScale) / 2);
        mesh.translateZ(-((boundingBox.max.z + boundingBox.min.z) * newScale) / 2);
    });

    // floor: mesh to receive shadows
    var floorTexture = new THREE.ImageUtils.loadTexture( './checkerboard.jpg' );
    floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping; 
    floorTexture.repeat.set( 10, 10 );
    // Note the change to Lambert material.
    var floorMaterial = new THREE.MeshLambertMaterial( { map: floorTexture, side: THREE.DoubleSide } );
    var floorGeometry = new THREE.PlaneGeometry(1000, 1000, 100, 100);
    var floor = new THREE.Mesh(floorGeometry, floorMaterial);
    floor.position.y = -80.5;
    floor.rotation.x = Math.PI / 2;
    floor.receiveShadow = true;
    scene.add(floor);
}

function animate() 
{
    requestAnimationFrame( animate );
    render();       
    update();
}

function update()
{   
    controls.update();
}

function render() 
{
    renderer.render( scene, camera );
}

</script>

</body>
</html>

And my output is like:

output image

I have also tried shader material and it gives me something like:Shadder output

What i want is: there should be a light from backside of object and the engrave part of object should glow (with respect to depth of object).

like image 427
Siraj ul Haq Avatar asked Nov 17 '15 09:11

Siraj ul Haq


1 Answers

This is by no means an answer, but I really think the best approach to this would be writing (or finding) a custom shader. I will preface this by saying that writing shaders is NOT child's play: it can become incredibly complex, due to shader programming languages being relatively low level and relying heavily on a knowledge of esoteric geometry / mathematics.


After some snooping, it looks like you'd need a shader that achieves something called Sub-Surface Scattering (known colloquially as SSS) - this is essentially the behavior of light through a translucent object, based on its thickness (and some other properties which I won't delve into):

Sub Surface Scattering (SSS)

To achieve a Lithophane effect, you'd need to generate a "thickness map" of sorts that maps to your object's mesh, and then the custom shader would scatter light correctly through this map to produce an effect similar to what you desire.

I learnt all of this from going through a principally simple presentation performed by the Lead Rendering Programmer at DICE games. Here's an example slide from the presentation:

DICE Game's presentation of their SSS shader implementation

This shader produces an effect that resembles this, based on thickness, in real time:

enter image description here

If you're serious about achieving this effect, I'd seriously recommend doing some reading on Cg or GLSL shader programming. Personally I like Cg, because i was incentivised to learn it for it's compatibility with Unity3D. For this reason, I found a great resource for learning Cg, namely the wikibook on the subject. It's (somewhat) specific to Unity, but the principles will be sound.

Either way I wish you luck on the subject. Hope this helps!

like image 153
jonny Avatar answered Sep 21 '22 22:09

jonny