conye – mocap

 

The one below is after I spent a kind of unnecessary amount of time cutting up a picture of myself to use as the reflection. I just thought their shaders were so cool and wanted to see what it would look like with a different image.

Here are some side by side pics with some different ball sizes:

My initial sketches:

As you can see, my final product doesn’t have much to do with my initial sketches. Originally, I wanted to display two figures made of particles walking towards each other, and the closer together they get, the more “attracted” to each other they are. I would’ve used a physics force formula to simulate this effect on their particles, where the effect is stronger the closer they are to each other. So, after the two figures meet in the middle they can no longer un-converge.

However, three.js was harder to work with then I realized, so I was super stuck the night it was due! Golan helped me/a few other people figure out three.js and said that using metaballs would look “slick.”

I thought using metaballs would be a good place to start (particle systems / point clouds seemed a little beyond my reach), so I attached some metaballs to a moving skeleton, but haven’t added the part with the two figures! I do like how it looks, but I really wish I had more time to play with the effects and finish my concept. I imagine it would look super “””””slick”””” if two metaball mocap figures collide and converge like I had imagined in my concept!!

Ultimately I learned a lot about how three.js works, along with how to use metaballs and shaders. I had originally wanted to spend more time making my own shader, but it was way too much to learn in one night (at least I have a basic understanding of how they work now though).

Below is the code. I’m using BVH files from the CMU graphics lab. It’s like a zombie child made of lots of three.js examples, but mainly the marching cubes example: https://threejs.org/examples/?q=mar#webgl_marchingcubes

		if (!Detector.webgl) Detector.addGetWebGLMessage();
 
		var MARGIN = 0;
 
		var SCREEN_WIDTH = window.innerWidth;
		var SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;
 
		var container, stats;
 
		var camera, scene, renderer;
 
		var mesh, texture, geometry, materials, material, current_material;
 
		var light, pointLight, ambientLight;
 
		var effect, resolution, numBlobs, bones;
 
		var composer, effectFXAA, hblur, vblur;
 
		var effectController;
 
		var time = 0;
		var clock = new THREE.Clock();
 
		init();
		animate();
 
		var mixer, skeletonHelper;
		var loader = new THREE.BVHLoader();
 
		loader.load("bvh/Brekel_03_11_2016_15_47_42_body1.bvh", function(result) {
 
		    skeletonHelper = new THREE.SkeletonHelper(result.skeleton.bones[0]);
		    skeletonHelper.skeleton = result.skeleton; // allow animation mixer to bind to SkeletonHelper directly
 
		    var boneContainer = new THREE.Group();
		    boneContainer.add(result.skeleton.bones[0]);
 
		    scene.add(skeletonHelper);
		    scene.add(boneContainer);
 
		    // play animation
		    mixer = new THREE.AnimationMixer(skeletonHelper);
		    mixer.clipAction(result.clip).setEffectiveWeight(1.0).play();
 
		});
 
 
		function init() {
		    scene = new THREE.Scene();
 
		    scene.add(new THREE.GridHelper(100, 10));
 
		    // renderer
		    renderer = new THREE.WebGLRenderer({ antialias: true });
		    renderer.setClearColor(0xeeeeee);
		    renderer.setPixelRatio(window.devicePixelRatio);
		    renderer.setSize(window.innerWidth, window.innerHeight);
 
		    document.body.appendChild(renderer.domElement);
 
		    window.addEventListener('resize', onWindowResize, false);
 
 
		    container = document.getElementById('container');
 
		    // CAMERA
 
		    camera = new THREE.PerspectiveCamera(45, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 10000);
		    camera.position.set(-500, 500, 1500);
 
		    // controls = new THREE.OrbitControls( camera );
 
		    // controls.autoRotate = true;
		    // controls.update();
 
		    // SCENE
 
		    scene = new THREE.Scene();
		    scene.background = new THREE.Color(0x050505);
 
		    // LIGHTS
 
		    light = new THREE.DirectionalLight(0xffffff);
		    light.position.set(0.5, 0.5, 1);
		    scene.add(light);
 
		    pointLight = new THREE.PointLight(0xff3300);
		    pointLight.position.set(0, 0, 100);
		    scene.add(pointLight);
 
		    ambientLight = new THREE.AmbientLight(0x080808);
		    scene.add(ambientLight);
 
		    // MATERIALS
 
		    materials = generateMaterials();
		    current_material = "shiny";
 
		    // MARCHING CUBES
 
		    resolution = 28;
		    numBlobs = 10;
 
		    effect = new THREE.MarchingCubes(resolution, materials[current_material].m, true, true);
		    effect.position.set(0, 0, 0);
		    effect.scale.set(500, 500, 500);
 
		    effect.enableUvs = false;
		    effect.enableColors = false;
 
		    scene.add(effect);
 
		    // RENDERER
 
		    renderer = new THREE.WebGLRenderer();
		    renderer.setPixelRatio(window.devicePixelRatio);
		    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
 
		    renderer.domElement.style.position = "absolute";
		    renderer.domElement.style.top = MARGIN + "px";
		    renderer.domElement.style.left = "0px";
 
		    container.appendChild(renderer.domElement);
 
		    //
 
		    renderer.gammaInput = true;
		    renderer.gammaOutput = true;
 
		    // CONTROLS
 
		    controls = new THREE.OrbitControls(camera, renderer.domElement);
		    controls.autoRotate = true;
		    controls.autoRotateSpeed = 12;
		    controls.update();
 
 
		    // COMPOSER
 
		    renderer.autoClear = false;
 
		    var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
		    var renderTarget = new THREE.WebGLRenderTarget(SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters);
 
		    effectFXAA = new THREE.ShaderPass(THREE.FXAAShader);
 
		    hblur = new THREE.ShaderPass(THREE.HorizontalTiltShiftShader);
		    vblur = new THREE.ShaderPass(THREE.VerticalTiltShiftShader);
 
		    var bluriness = 8;
 
		    hblur.uniforms['h'].value = bluriness / SCREEN_WIDTH;
		    vblur.uniforms['v'].value = bluriness / SCREEN_HEIGHT;
 
		    hblur.uniforms['r'].value = vblur.uniforms['r'].value = 0.5;
 
		    effectFXAA.uniforms['resolution'].value.set(1 / SCREEN_WIDTH, 1 / SCREEN_HEIGHT);
 
		    var renderModel = new THREE.RenderPass(scene, camera);
 
		    vblur.renderToScreen = true;
		    //effectFXAA.renderToScreen = true;
 
		    composer = new THREE.EffectComposer(renderer, renderTarget);
 
		    composer.addPass(renderModel);
 
		    composer.addPass(effectFXAA);
 
		    composer.addPass(hblur);
		    composer.addPass(vblur);
 
		    // GUI
 
		    setupGui();
 
		    // EVENTS
 
		    window.addEventListener('resize', onWindowResize, false);
 
		}
 
		//
 
		function onWindowResize(event) {
 
		    SCREEN_WIDTH = window.innerWidth;
		    SCREEN_HEIGHT = window.innerHeight - 2 * MARGIN;
 
		    camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT;
		    camera.updateProjectionMatrix();
 
		    renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
		    composer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
 
		    hblur.uniforms['h'].value = 4 / SCREEN_WIDTH;
		    vblur.uniforms['v'].value = 4 / SCREEN_HEIGHT;
 
		    effectFXAA.uniforms['resolution'].value.set(1 / SCREEN_WIDTH, 1 / SCREEN_HEIGHT);
 
		}
 
		function generateMaterials() {
 
		    // environment map
 
		    var path = "textures/SwedishRoyalCastle/";
		    var format = '.jpg';
		    var urls = [
		        path + 'px' + format, path + 'nx' + format,
		        path + 'py' + format, path + 'ny' + format,
		        path + 'pz' + format, path + 'nz' + format
		    ];
 
		    var cubeTextureLoader = new THREE.CubeTextureLoader();
 
		    var reflectionCube = cubeTextureLoader.load(urls);
		    reflectionCube.format = THREE.RGBFormat;
 
		    var refractionCube = cubeTextureLoader.load(urls);
		    reflectionCube.format = THREE.RGBFormat;
		    refractionCube.mapping = THREE.CubeRefractionMapping;
 
		    // toons
 
		    var toonMaterial1 = createShaderMaterial("toon1", light, ambientLight),
		        toonMaterial2 = createShaderMaterial("toon2", light, ambientLight),
		        hatchingMaterial = createShaderMaterial("hatching", light, ambientLight),
		        hatchingMaterial2 = createShaderMaterial("hatching", light, ambientLight),
		        dottedMaterial = createShaderMaterial("dotted", light, ambientLight),
		        dottedMaterial2 = createShaderMaterial("dotted", light, ambientLight);
 
		    hatchingMaterial2.uniforms.uBaseColor.value.setRGB(0, 0, 0);
		    hatchingMaterial2.uniforms.uLineColor1.value.setHSL(0, 0.8, 0.5);
		    hatchingMaterial2.uniforms.uLineColor2.value.setHSL(0, 0.8, 0.5);
		    hatchingMaterial2.uniforms.uLineColor3.value.setHSL(0, 0.8, 0.5);
		    hatchingMaterial2.uniforms.uLineColor4.value.setHSL(0.1, 0.8, 0.5);
 
		    dottedMaterial2.uniforms.uBaseColor.value.setRGB(0, 0, 0);
		    dottedMaterial2.uniforms.uLineColor1.value.setHSL(0.05, 1.0, 0.5);
 
		    var texture = new THREE.TextureLoader().load("textures/UV_Grid_Sm.jpg");
		    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
 
		    var materials = {
 
		        "shiny": {
		            m: new THREE.MeshStandardMaterial({ color: 0x550000, envMap: reflectionCube, roughness: 0.1, metalness: 1.0 }),
		            h: 0,
		            s: 0.9,
		            l: 0.3
		        },
 
		    };
 
		    return materials;
 
		}
 
		function createShaderMaterial(id, light, ambientLight) {
 
		    var shader = THREE.ShaderToon[id];
 
		    var u = THREE.UniformsUtils.clone(shader.uniforms);
 
		    var vs = shader.vertexShader;
		    var fs = shader.fragmentShader;
 
		    var material = new THREE.ShaderMaterial({ uniforms: u, vertexShader: vs, fragmentShader: fs });
 
		    material.uniforms.uDirLightPos.value = light.position;
		    material.uniforms.uDirLightColor.value = light.color;
 
		    material.uniforms.uAmbientLightColor.value = ambientLight.color;
 
		    return material;
 
		}
 
		//
 
		function setupGui() {
 
		    var createHandler = function(id) {
 
		        return function() {
 
		            var mat_old = materials[current_material];
		            mat_old.h = m_h.getValue();
		            mat_old.s = m_s.getValue();
		            mat_old.l = m_l.getValue();
 
		            current_material = id;
 
		            var mat = materials[id];
		            effect.material = mat.m;
 
		            m_h.setValue(mat.h);
		            m_s.setValue(mat.s);
		            m_l.setValue(mat.l);
 
		            effect.enableUvs = (current_material === "textured") ? true : false;
		            effect.enableColors = (current_material === "colors") ? true : false;
 
		        };
 
		    };
 
		    effectController = {
 
		        material: "shiny",
 
		        speed: 1.0,
		        numBlobs: 38,
		        resolution: 120,
		        isolation: 20,
 
		        floor: false,
		        wallx: false,
		        wallz: false,
 
		        hue: 0.0,
		        saturation: 0.5,
		        lightness: 0.3,
 
		        lhue: 0.0,
		        lsaturation: 1.0,
		        llightness: 0.3,
 
		        lx: 0.5,
		        ly: 0.5,
		        lz: 0.5,
 
		        postprocessing: false,
 
		        dummy: function() {}
 
		    };
 
		}
		// this controls content of marching cubes voxel field
 
		function updateCubes(object, time, numblobs, floor, wallx, wallz) {
 
		    object.reset();
 
		    var i, ballx, bally, ballz, subtract, strength;
 
		    subtract = 10;
		    strength = 1 / ((Math.sqrt(numblobs) - 1) / 4 + 1);
 
 
		    if (skeletonHelper) {
		        var mySkeleton = skeletonHelper.skeleton;
		        var myBones = mySkeleton.bones;
		        numblobs = myBones.length;
		        for (var i = 0; i < numblobs; i++) { var modstrength = strength; if (i > 14 && i < 58) modstrength = 0.1;
		            var bone = myBones[i].getWorldPosition();
		            if (bone.x < -130 || bone.y < 0 || bone.z < -300) console.log("!!!");
		            ballx = bone.x.map(-130, 100, 0, 1) * 0.5 + 0.3;
		            bally = bone.y.map(0, 400, 0, 1) * 0.8 + 0.1;
		            ballz = bone.z.map(-300, 0, 0, 1) * 0.7;
		            object.addBall(ballx, bally, ballz, modstrength, subtract);
		        }
		    }
 
		    if (floor) object.addPlaneY(2, 12);
		    // if ( wallz ) object.addPlaneZ( 2, 12 );
		    // if ( wallx ) object.addPlaneX( 2, 12 );
 
		}
 
 
		function animate() {
 
		    requestAnimationFrame(animate);
 
		    controls.update();
		    var delta = clock.getDelta();
 
		    if (mixer) mixer.update(delta);
		    if (skeletonHelper) {
		        skeletonHelper.update();
		        skeletonHelper.material.visible = false;
		    }
 
		    render();
 
		    // renderer.render( scene, camera );
 
		    // if(skeletonHelper){
		    // 		var mySkeleton = skeletonHelper.skeleton;
		    // 		var myBones = mySkeleton.bones;
		    // 		var handBone = myBones[10].position;
		    // 		console.log(handBone);
		    // }
		}
 
		function render() {
 
		    var delta = clock.getDelta();
 
		    time += delta * effectController.speed * 0.5;
 
		    // marching cubes
 
		    if (effectController.resolution !== resolution) {
 
		        resolution = effectController.resolution;
		        effect.init(Math.floor(resolution));
 
		    }
 
		    if (effectController.isolation !== effect.isolation) {
 
		        effect.isolation = effectController.isolation;
 
		    }
 
		    updateCubes(effect, time, effectController.numBlobs, effectController.floor, effectController.wallx, effectController.wallz);
 
		    // materials
 
		    if (effect.material instanceof THREE.ShaderMaterial) {
 
		        if (current_material === "dotted2") {
 
		            effect.material.uniforms.uLineColor1.value.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
 
		        } else if (current_material === "hatching2") {
 
		            var u = effect.material.uniforms;
 
		            u.uLineColor1.value.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
		            u.uLineColor2.value.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
		            u.uLineColor3.value.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
		            u.uLineColor4.value.setHSL((effectController.hue + 0.2 % 1.0), effectController.saturation, effectController.lightness);
 
		        } else {
 
		            effect.material.uniforms.uBaseColor.value.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
 
		        }
 
		    } else {
 
		        effect.material.color.setHSL(effectController.hue, effectController.saturation, effectController.lightness);
 
		    }
 
		    // lights
 
		    light.position.set(effectController.lx, effectController.ly, effectController.lz);
		    light.position.normalize();
 
		    pointLight.color.setHSL(effectController.lhue, effectController.lsaturation, effectController.llightness);
 
		    // render
 
		    renderer.render(scene, camera);
 
		}
 
 
		function tween(light) {
		    new TWEEN.Tween(light).to({
		            angle: (Math.random() * 0.7) + 0.1,
		            penumbra: Math.random() + 1
		        }, Math.random() * 3000 + 2000)
		        .easing(TWEEN.Easing.Quadratic.Out).start();
		    new TWEEN.Tween(light.position).to({
		            x: (Math.random() * 30) - 15,
		            y: (Math.random() * 10) + 15,
		            z: (Math.random() * 30) - 15
		        }, Math.random() * 3000 + 2000)
		        .easing(TWEEN.Easing.Quadratic.Out).start();
		}
 
		Number.prototype.map = function(in_min, in_max, out_min, out_max) {
		    var temp = (this - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
		    if (temp < 0) return 0; if (temp > out_max) return out_max;
		    return temp;
		}