Three.js / Webgl X-RAY effect

血红的双手。 提交于 2019-12-08 02:26:48

问题


How to achieve an x-ray-style effect in three.js / webgl? Some sort of this

UPD

I need real-time render with this stuff, not a still image. This can be done with shaders, that change density in non-linear way on overlaps based on distance. I briefly understand theory, but have no practice, that is why I need help with this


回答1:


Ok, got acceptable result with this:

<!DOCTYPE html>
<html>

<head>
    <title>X-ray</title>
    <script type="text/javascript" src="js/three.js/build/three.js"></script>
    <script type="text/javascript" src="js/three.js/examples/js/loaders/OBJLoader.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script type="text/javascript" src="js/stats.min.js"></script>
    <script type="text/javascript" src="js/three.js/examples/js/renderers/SVGRenderer.js"></script>

    <script id="vertexShader" type="x-shader/x-vertex">
        uniform vec3 viewVector;
        uniform float c;
        uniform float p;
        varying float intensity;
        void main()
        {
            vec3 vNormal = normalize( normalMatrix * normal );
            vec3 vNormel = normalize( normalMatrix * viewVector );
            intensity = pow( c - dot(vNormal, vNormel), p );

            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }
    </script>

    <!-- fragment shader a.k.a. pixel shader -->
    <script id="fragmentShader" type="x-shader/x-vertex">
        uniform vec3 glowColor;
        varying float intensity;
        void main()
        {
            vec3 glow = glowColor * intensity;
            gl_FragColor = vec4( glow, 1.0 );
        }
    </script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */

            margin: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">
    // once everything is loaded, we run our Three.js stuff.
    $(function () {
        var mouseX = 0, mouseY = 0;
        var human;
        camstep = 0;

        var stats = initStats();

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        var scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render and set the size
        var renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});

        renderer.setClearColor(0x000000, 1.0);
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;
        renderer.shadowMapType = THREE.PCFShadowMap;


        materialCameraPosition = camera.position.clone();
        materialCameraPosition.z += 10;

        // position and point the camera to the center of the scene
        camera.position.x = -10;
        camera.position.y = 0;
        camera.position.z = 15;
        lookAt = scene.position;
        lookAt.y = 15;
        camera.lookAt(lookAt);

        // add subtle ambient lighting
        var ambientLight = new THREE.AmbientLight(0x0c0c0c);
        //scene.add(ambientLight);


        // add the output of the renderer to the html element
        $("#WebGL-output").append(renderer.domElement);

        var customMaterial = new THREE.ShaderMaterial(
                {
                    uniforms: {
                        "c": { type: "f", value: 1.0 },
                        "p": { type: "f", value: 3 },
                        glowColor: { type: "c", value: new THREE.Color(0x84ccff) },
                        viewVector: { type: "v3", value: materialCameraPosition }
                    },
                    vertexShader: document.getElementById('vertexShader').textContent,
                    fragmentShader: document.getElementById('fragmentShader').textContent,
                    side: THREE.FrontSide,
                    blending: THREE.AdditiveBlending,
                    transparent: true,
                    //opacity: 0.5,
                    depthWrite: false
                });

        var manager = new THREE.LoadingManager();
        manager.onProgress = function (item, loaded, total) {
            console.log(item, loaded, total);
        };
        var loader = new THREE.OBJLoader(manager);
        loader.load('body_anatomy3.obj', function (object) {
            console.log(object);
            object.traverse(function (child) {
                if (child instanceof THREE.Mesh) {
                    console.log(child);
                    child.material = customMaterial;
                }
            });
            object.position.y = 4;
            object.scale.x = 0.01;
            object.scale.y = 0.01;
            object.scale.z = 0.01;
            human = object;
            scene.add(human);
        });


        // call the render function
        var step = 0;
        render();

        function render() {
            stats.update();

            camstep += 0.02;

            camera.position.x = -20 * (Math.cos(camstep));
            camera.position.z = (20 * (Math.sin(camstep)));
            camera.position.y = 20;

            camera.lookAt(lookAt);

            if (human) {
                //human.rotation.y += 0.02;
                materialCameraPosition = camera.position.clone();
                materialCameraPosition.z += 10;
                human.traverse(function (child) {
                    if (child instanceof THREE.Mesh) {
                        //console.log(child.material.uniforms.viewVector);
                        child.material.uniforms.viewVector.value =
                                new THREE.Vector3().subVectors(camera.position, human.position);
                    }
                });
            }

            //sphere.material.uniforms.viewVector.value = new THREE.Vector3().subVectors(camera.position, sphere.position);

            // render using requestAnimationFrame
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms

            // Align top-left
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            $("#Stats-output").append(stats.domElement);

            return stats;
        }
    });
</script>
</body>

</html>




回答2:


This is the as Владимир Корнилов's example except I changed the shader a little.

I'm not sure what he was going for with the dot(vNormal, vNormel). Doing abs(dot(vNormal, vec3(0, 0, 1)) will give you something that is brighter when facing toward or away from the view. Making it 1.0 - abs(dot(vNormal, vec3(0, 0, 1)) will flip that so perpendicular to the view is brighter. Then add the pow and it looks better to me but I guess that's subjective

var human;

var $ = document.querySelector.bind(document);

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
var renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
renderer.setClearColor(0x000000, 1.0);

lookAt = scene.position;
lookAt.y = 15;
camera.lookAt(lookAt);

document.body.appendChild(renderer.domElement);

var customMaterial = new THREE.ShaderMaterial(
  {
    uniforms: {
      p: { type: "f", value: 2 },
      glowColor: { type: "c", value: new THREE.Color(0x84ccff) },
    },
    vertexShader: $('#vertexShader').text,
    fragmentShader: $('#fragmentShader').text,
    side: THREE.DoubleSide,
    blending: THREE.AdditiveBlending,
    transparent: true,
    depthWrite: false
  });

var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load('http://greggman.github.io/doodles/assets/woman.dae', function (collada) {
  dae = collada.scene;

  dae.traverse( function ( child ) {

    if (child instanceof THREE.Mesh) {
      console.log(child);
      child.material = customMaterial;
    }

  } );

  dae.scale.x = 0.2;
  dae.scale.y = 0.2;
  dae.scale.z = 0.2;
  human = dae;
  scene.add(human);
});


function resize() {
  var canvas = renderer.domElement;
  var width  = canvas.clientWidth;
  var height = canvas.clientHeight;
  if (canvas.width !== width || canvas.height !== height) {
    renderer.setSize(width, height, false);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
  }
}
    
// call the render function
function render(time) {
  time *= 0.001;
  
  resize();

  camera.position.x = -20 * (Math.cos(time));
  camera.position.z = (20 * (Math.sin(time)));
  camera.position.y = 20;

  camera.lookAt(lookAt);

  renderer.render(scene, camera);
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r71/three.min.js"></script>
<script src="//greggman.github.io/doodles/js/three/js/loaders/ColladaLoader.js"></script>

<script id="vertexShader" type="x-shader/x-vertex">
        uniform float p;
        varying float intensity;
        void main()
        {
            vec3 vNormal = normalize( normalMatrix * normal );
            intensity = pow(1.0 - abs(dot(vNormal, vec3(0, 0, 1))), p);
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }
</script>

<!-- fragment shader a.k.a. pixel shader -->
<script id="fragmentShader" type="x-shader/x-vertex">
        uniform vec3 glowColor;
        varying float intensity;
        void main()
        {
            vec3 glow = glowColor * intensity;
            gl_FragColor = vec4( glow, 1.0 );
        }
</script>
<style>
  html, body {
    margin: 0;
    overflow: hidden;
    height: 100%;
  }
  canvas {
    width: 100%;
    height: 100%;
  }
</style>



回答3:


At the moment got close result with glow shader based on this demo http://stemkoski.github.io/Three.js/Shader-Glow.html



来源:https://stackoverflow.com/questions/26336585/three-js-webgl-x-ray-effect

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!