Log Stash

as an Industrial Personnel

프로그래밍

라인하르트 방어막 셰이더 흉내내기

SavvyTuna 2017. 11. 27. 23:58

Three.js로 만들어봄. 깊이맵으로 가까이 있으면 glow 효과 나오게 함. (gl_FragCoord의 z값 처리는 아직 안 함)

Code

Vertex shader

void main()
{
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

Fragment Shader

// Screen size
uniform sampler2D depthMap;
uniform vec2 screenSize;

void main()
{
    float depth = texture2D(depthMap, gl_FragCoord.xy / screenSize).r;
    float logDiff = log(1000.0 * abs(gl_FragCoord.z - depth));
    float glow = clamp(1.0 - logDiff, 0.0, 1.0);
    float alpha = 0.5 + glow;

    gl_FragColor = vec4(0.27450982 + glow, 0.64705884 + glow, 0.9647059 + glow, alpha);
}

메인 스크립트 (typescript)

import * as THREE from "./lib/three.module";

class ShieldShaderEx {

    readonly screenWidth: number = 600;
    readonly screenHeight: number = 400;
    readonly screenSize: THREE.Vector2 = new THREE.Vector2(this.screenWidth, this.screenHeight);

    scene:    THREE.Scene;
    renderer: THREE.WebGLRenderer;
    camera:   THREE.Camera;
    light:    THREE.DirectionalLight;

    loader: THREE.TextureLoader;

    depthTarget: THREE.WebGLRenderTarget;

    torusMesh:  THREE.Mesh;
    shieldMesh: THREE.Mesh;

    clock: THREE.Clock;

    constructor() {

        // init loader
        this.loader = new THREE.TextureLoader();
        this.loader.setCrossOrigin("anonymous");

        // init renderer
        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(this.screenWidth, this.screenHeight);
        this.renderer.setClearColor(0xdbdbdb);

        document.body.appendChild(this.renderer.domElement);

        // init scene and camera
        this.scene = new THREE.Scene();

        const aspectRatio: number = this.screenWidth / this.screenHeight;
        this.camera = new THREE.PerspectiveCamera(40, aspectRatio, 1, 10000);
        this.camera.position.set(20, 20, 20);
        this.camera.lookAt(new THREE.Vector3(0, 0, 0));

        // init depth rendertarget
        this.depthTarget = new THREE.WebGLRenderTarget(this.screenWidth, this.screenHeight);
        this.depthTarget.depthTexture = new THREE.DepthTexture(this.screenWidth, this.screenHeight);

        // init directional light
        this.light = new THREE.DirectionalLight(0xffffff, 1);
        this.light.position.set(20, 50, 0);
        this.scene.add(this.light);

        this.initMeshes();

        this.clock = new THREE.Clock();
    }

    initMeshes() {

        // 1. create torus
        {
            // geometry & material
            let geometry: THREE.TorusKnotGeometry = new THREE.TorusKnotGeometry( 3, 1, 100, 64 );
            let material: THREE.Material = new THREE.MeshBasicMaterial({ 
                color: 0x44c99d,
            });

            // mesh
            this.torusMesh = new THREE.Mesh(geometry, material);
            this.scene.add(this.torusMesh);

            this.torusMesh.position.x -= 8;
        }

        // 2. create shield
        {
            let vertexShader = document.getElementById('vertexshader').innerHTML;
            let fragShader = document.getElementById('fragmentshader').innerHTML

            let geometry: THREE.PlaneGeometry = new THREE.PlaneGeometry(20, 15);
            let material: THREE.ShaderMaterial = new THREE.ShaderMaterial({
                uniforms: {
                    depthMap: { type: "t", value: this.depthTarget.depthTexture },
                    screenSize: { type: "v", value: this.screenSize },
                },
                vertexShader: vertexShader,
                fragmentShader: fragShader,
                transparent: true,
                side: THREE.DoubleSide,
            });

            this.shieldMesh = new THREE.Mesh(geometry, material);
            this.scene.add(this.shieldMesh);
        }
    }

    update(delta: number) {

        if (typeof this.torusMesh !== "undefined") {
            this.torusMesh.rotation.x += 0.6 * delta;
            this.torusMesh.rotation.y += 0.06 * delta;
            this.torusMesh.rotation.z += 0.3 * delta;
        }

        if (typeof this.shieldMesh !== "undefined")
            this.shieldMesh.rotation.y -= 0.6 * delta;
    }

    render() {
        requestAnimationFrame(() => this.render());

        let delta: number = this.clock.getDelta();
        this.update(delta);

        // draw depthmap
        if (typeof this.shieldMesh !== "undefined") {
            this.shieldMesh.visible = false;
            this.renderer.render(this.scene, this.camera, this.depthTarget);
            this.shieldMesh.visible = true;
        }

        // draw main scene
        this.renderer.render(this.scene, this.camera);
    }

    run() {
        this.render();
    }
}

let example = new ShieldShaderEx();
example.run();