상세 컨텐츠

본문 제목

[WebGL]Three.js - LineBasicMaterial의 lineWidth작동 안될때 따로 나온 THREE.MeshLine을 사용하거나, THREE.MeshLine을 모듈로 바꾸어 이용하기 관련

WEB/WebGL

by AlrepondTech 2018. 6. 11. 11:10

본문

반응형
728x170

 

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

 

 

WebGL 에서 굵은 선을 나타낸 매쉬를 이용하려고 "LineBasicMaterial""lineWidth" 을 이용하려고 하는데

 

대략 코드는 이렇다.

 

     let obj3DMeBG = new THREE.Object3D();

 

    var materialBG = new THREE.LineBasicMaterial( { color: 0xaaff0f, opacity:1, linewidth:5 } );

    var line = new THREE.Line(geometry, materialBG);

    objMeBG.add(line);

 

하지만 "lineWidth" 선의 굵기 부분이 제대로 동작을 하지 않는것이다. 구글에 찾아보니 "lineWidth" 는 무슨 문제가 있어서

 

작동이 잘 되지 않는다고 하는 것이다. 그래서 대안이 Three를 이용한 

 

JS "THREE.MeshLine"

 

링크: https://github.com/spite/THREE.MeshLine

 

코드다운로드: 

THREE.MeshLine-master.zip
다운로드

 

 

여기에서 지원하는 THREE.MeshLine를 이용하면 된다.

 

 

여기서 보면 예제가 나오는 데로

var geometry= new THREE.Geometry();

        geometry.vertices.push(

            new THREE.Vector3( -0.5, 0.5, 0 ),

            new THREE.Vector3( 0.5, 0.5, 0 ),

            new THREE.Vector3( 0.5, -0.5, 0 ),

            new THREE.Vector3( -0.5, -0.5, 0 ),

            new THREE.Vector3( -0.5, 0.5, 0 ),

        );

   

   let gFig = new MeshLine();

 

   gFig.setGeometry( geometry); 

 

   let obj3DMeBG = new THREE.Object3D();

   let resolution = new THREE.Vector2( 800, 600); //화면비율 width, height

 

   let opt = {

                color: new THREE.Color( 0xff0000 ), //컬러 부분은 THREE.Color 써야한다.

                opacity: 1,

                resolution: resolution,

                lineWidth: 1/1000, //화면대비 비율로 굵기가 정해진다. (대충 1000으로 정함)

            };

 

   var material = new MeshLineMaterial(OPTIONS);

 

   let meshFig = new THREE.Mesh(gFig.geometry, material );

   meshFig.position.set(0.0, 0.0, 0.0);

   obj3DMeBG .add( meshFig );

 

이런식으로 하면된다.

 

 

 

=================================

=================================

=================================

 

 

 

JS "THREE.MeshLine" 모듈화 과련

 

링크: https://github.com/spite/THREE.MeshLine

 

이부분을 ES6 의 클래스을 사용 하여 모듈화부분으로 개조 하였다.

 

요즘 추세에 맞게 Class로 모듈화 하여 만들어 보았다.

 

파일링크:  

THREE.MeshLine.module.js
다운로드

 

 

수정 내용:

 

//import 부분에 자신의 JS라이브러리 Three부분을 링크한 위치를 적어두면 된다.

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

 

export class MeshLineModule

{

    constructor()

    {

        this.positions = [];

 

        this.previous = [];

        this.next = [];

        this.side = [];

        this.width = [];

        this.indices_array = [];

        this.uvs = [];

        this.counters = [];

        this.geometry = new THREE.BufferGeometry();

 

        this.widthCallback = null;

    }

 

    setGeometry(g, c)

    {

        this.widthCallback = c;

 

        this.positions = [];

        this.counters = [];

 

        if( g instanceof THREE.Geometry ) {

            for( let j = 0; j < g.vertices.length; j++ ) {

                let v = g.vertices[ j ];

                let c = j/g.vertices.length;

                this.positions.push( v.x, v.y, v.z );

                this.positions.push( v.x, v.y, v.z );

                this.counters.push(c);

                this.counters.push(c);

            }

        }

 

        if( g instanceof THREE.BufferGeometry ) {

            // read attribute positions ?

        }

 

        if( g instanceof Float32Array || g instanceof Array ) {

            for( let j = 0; j < g.length; j += 3 ) {

                let c = j/g.length;

                this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );

                this.positions.push( g[ j ], g[ j + 1 ], g[ j + 2 ] );

                this.counters.push(c);

                this.counters.push(c);

            }

        }

 

        this.process();

    }

 

    compareV3( a, b )

    {

        let aa = a * 6;

        let ab = b * 6;

        return ( this.positions[ aa ] === this.positions[ ab ] ) && ( this.positions[ aa + 1 ] === this.positions[ ab + 1 ] ) && ( this.positions[ aa + 2 ] === this.positions[ ab + 2 ] );

    }

 

    copyV3( a )

    {

        let aa = a * 6;

        return [ this.positions[ aa ], this.positions[ aa + 1 ], this.positions[ aa + 2 ] ];

    }

 

    process()

    {

        let l = this.positions.length / 6;

 

        this.previous = [];

        this.next = [];

        this.side = [];

        this.width = [];

        this.indices_array = [];

        this.uvs = [];

 

        for( let j = 0; j < l; j++ ) {

            this.side.push( 1 );

            this.side.push( -1 );

        }

 

        let w;

        for( let j = 0; j < l; j++ ) {

            if( this.widthCallback ) w = this.widthCallback( j / ( l -1 ) );

            else w = 1;

            this.width.push( w );

            this.width.push( w );

        }

 

        for( let j = 0; j < l; j++ ) {

            this.uvs.push( j / ( l - 1 ), 0 );

            this.uvs.push( j / ( l - 1 ), 1 );

        }

 

        let v;

 

        if( this.compareV3( 0, l - 1 ) ){

            v = this.copyV3( l - 2 );

        } else {

            v = this.copyV3( 0 );

        }

        this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );

        this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );

        for( let j = 0; j < l - 1; j++ ) {

            v = this.copyV3( j );

            this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );

            this.previous.push( v[ 0 ], v[ 1 ], v[ 2 ] );

        }

 

        for( let j = 1; j < l; j++ ) {

            v = this.copyV3( j );

            this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );

            this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );

        }

 

        if( this.compareV3( l - 1, 0 ) ){

            v = this.copyV3( 1 );

        } else {

            v = this.copyV3( l - 1 );

        }

        this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );

        this.next.push( v[ 0 ], v[ 1 ], v[ 2 ] );

 

        for( let j = 0; j < l - 1; j++ ) {

            let n = j * 2;

            this.indices_array.push( n, n + 1, n + 2 );

            this.indices_array.push( n + 2, n + 1, n + 3 );

        }

 

        if (!this.attributes) {

            this.attributes = {

                position: new THREE.BufferAttribute( new Float32Array( this.positions ), 3 ),

                previous: new THREE.BufferAttribute( new Float32Array( this.previous ), 3 ),

                next: new THREE.BufferAttribute( new Float32Array( this.next ), 3 ),

                side: new THREE.BufferAttribute( new Float32Array( this.side ), 1 ),

                width: new THREE.BufferAttribute( new Float32Array( this.width ), 1 ),

                uv: new THREE.BufferAttribute( new Float32Array( this.uvs ), 2 ),

                index: new THREE.BufferAttribute( new Uint16Array( this.indices_array ), 1 ),

                counters: new THREE.BufferAttribute( new Float32Array( this.counters ), 1 )

            }

        } else {

            this.attributes.position.copyArray(new Float32Array(this.positions));

            this.attributes.position.needsUpdate = true;

            this.attributes.previous.copyArray(new Float32Array(this.previous));

            this.attributes.previous.needsUpdate = true;

            this.attributes.next.copyArray(new Float32Array(this.next));

            this.attributes.next.needsUpdate = true;

            this.attributes.side.copyArray(new Float32Array(this.side));

            this.attributes.side.needsUpdate = true;

            this.attributes.width.copyArray(new Float32Array(this.width));

            this.attributes.width.needsUpdate = true;

            this.attributes.uv.copyArray(new Float32Array(this.uvs));

            this.attributes.uv.needsUpdate = true;

            this.attributes.index.copyArray(new Uint16Array(this.indices_array));

            this.attributes.index.needsUpdate = true;

        }

 

        this.geometry.addAttribute( 'position', this.attributes.position );

        this.geometry.addAttribute( 'previous', this.attributes.previous );

        this.geometry.addAttribute( 'next', this.attributes.next );

        this.geometry.addAttribute( 'side', this.attributes.side );

        this.geometry.addAttribute( 'width', this.attributes.width );

        this.geometry.addAttribute( 'uv', this.attributes.uv );

        this.geometry.addAttribute( 'counters', this.attributes.counters );

 

        this.geometry.setIndex( this.attributes.index );

    }

 

 

    memcpy (src, srcOffset, dst, dstOffset, length)

    {

        let i;

 

        src = src.subarray || src.slice ? src : src.buffer

        dst = dst.subarray || dst.slice ? dst : dst.buffer

 

        src = srcOffset ? src.subarray ?

            src.subarray(srcOffset, length && srcOffset + length) :

            src.slice(srcOffset, length && srcOffset + length) : src

 

        if (dst.set) {

            dst.set(src, dstOffset)

        } else {

            for (i=0; i<src.length; i++) {

                dst[i + dstOffset] = src[i]

            }

        }

 

        return dst

    }

 

    /**

     * Fast method to advance the line by one position.  The oldest position is removed.

     * @param position

     */

    advance( position )

    {

        let positions = this.attributes.position.array;

        let previous = this.attributes.previous.array;

        let next = this.attributes.next.array;

        let l = positions.length;

 

        // PREVIOUS

        this.memcpy( positions, 0, previous, 0, l );

 

        // POSITIONS

        this.memcpy( positions, 6, positions, 0, l - 6 );

 

        positions[l - 6] = position.x;

        positions[l - 5] = position.y;

        positions[l - 4] = position.z;

        positions[l - 3] = position.x;

        positions[l - 2] = position.y;

        positions[l - 1] = position.z;

 

        // NEXT

        this.memcpy( positions, 6, next, 0, l - 6 );

 

        next[l - 6]  = position.x;

        next[l - 5]  = position.y;

        next[l - 4]  = position.z;

        next[l - 3]  = position.x;

        next[l - 2]  = position.y;

        next[l - 1]  = position.z;

 

        this.attributes.position.needsUpdate = true;

        this.attributes.previous.needsUpdate = true;

        this.attributes.next.needsUpdate = true;

    }

 

    //MeshLineMaterial.prototype = Object.create( THREE.Material.prototype );

    //MeshLineMaterial.prototype.constructor = MeshLineMaterial;

}

 

export class MeshLineMaterialModule extends THREE.Material

{

    constructor( parameters = {} )

    {

        super();

 

        let vertexShaderSource = [

            'precision highp float;',

            '',

            'attribute vec3 position;',

            'attribute vec3 previous;',

            'attribute vec3 next;',

            'attribute float side;',

            'attribute float width;',

            'attribute vec2 uv;',

            'attribute float counters;',

            '',

            'uniform mat4 projectionMatrix;',

            'uniform mat4 modelViewMatrix;',

            'uniform vec2 resolution;',

            'uniform float lineWidth;',

            'uniform vec3 color;',

            'uniform float opacity;',

            'uniform float near;',

            'uniform float far;',

            'uniform float sizeAttenuation;',

            '',

            'varying vec2 vUV;',

            'varying vec4 vColor;',

            'varying float vCounters;',

            '',

            'vec2 fix( vec4 i, float aspect ) {',

            '',

            '    vec2 res = i.xy / i.w;',

            '    res.x *= aspect;',

            ' vCounters = counters;',

            '    return res;',

            '',

            '}',

            '',

            'void main() {',

            '',

            '    float aspect = resolution.x / resolution.y;',

            ' float pixelWidthRatio = 1. / (resolution.x * projectionMatrix[0][0]);',

            '',

            '    vColor = vec4( color, opacity );',

            '    vUV = uv;',

            '',

            '    mat4 m = projectionMatrix * modelViewMatrix;',

            '    vec4 finalPosition = m * vec4( position, 1.0 );',

            '    vec4 prevPos = m * vec4( previous, 1.0 );',

            '    vec4 nextPos = m * vec4( next, 1.0 );',

            '',

            '    vec2 currentP = fix( finalPosition, aspect );',

            '    vec2 prevP = fix( prevPos, aspect );',

            '    vec2 nextP = fix( nextPos, aspect );',

            '',

            ' float pixelWidth = finalPosition.w * pixelWidthRatio;',

            '    float w = 1.8 * pixelWidth * lineWidth * width;',

            '',

            '    if( sizeAttenuation == 1. ) {',

            '        w = 1.8 * lineWidth * width;',

            '    }',

            '',

            '    vec2 dir;',

            '    if( nextP == currentP ) dir = normalize( currentP - prevP );',

            '    else if( prevP == currentP ) dir = normalize( nextP - currentP );',

            '    else {',

            '        vec2 dir1 = normalize( currentP - prevP );',

            '        vec2 dir2 = normalize( nextP - currentP );',

            '        dir = normalize( dir1 + dir2 );',

            '',

            '        vec2 perp = vec2( -dir1.y, dir1.x );',

            '        vec2 miter = vec2( -dir.y, dir.x );',

            '        //w = clamp( w / dot( miter, perp ), 0., 4. * lineWidth * width );',

            '',

            '    }',

            '',

            '    //vec2 normal = ( cross( vec3( dir, 0. ), vec3( 0., 0., 1. ) ) ).xy;',

            '    vec2 normal = vec2( -dir.y, dir.x );',

            '    normal.x /= aspect;',

            '    normal *= .5 * w;',

            '',

            '    vec4 offset = vec4( normal * side, 0.0, 1.0 );',

            '    finalPosition.xy += offset.xy;',

            '',

            '    gl_Position = finalPosition;',

            '',

            '}' ];

 

        let fragmentShaderSource = [

            '#extension GL_OES_standard_derivatives : enable',

            'precision mediump float;',

            '',

            'uniform sampler2D map;',

            'uniform sampler2D alphaMap;',

            'uniform float useMap;',

            'uniform float useAlphaMap;',

            'uniform float useDash;',

            'uniform float dashArray;',

            'uniform float dashOffset;',

            'uniform float dashRatio;',

            'uniform float visibility;',

            'uniform float alphaTest;',

            'uniform vec2 repeat;',

            '',

            'varying vec2 vUV;',

            'varying vec4 vColor;',

            'varying float vCounters;',

            '',

            'void main() {',

            '',

            '    vec4 c = vColor;',

            '    if( useMap == 1. ) c *= texture2D( map, vUV * repeat );',

            '    if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUV * repeat ).a;',

            '    if( c.a < alphaTest ) discard;',

            '    if( useDash == 1. ){',

            '        c.a *= ceil(mod(vCounters + dashOffset, dashArray) - (dashArray * dashRatio));',

            '    }',

            '    gl_FragColor = c;',

            '    gl_FragColor.a *= step(vCounters, visibility);',

            '}' ];

 

        this.lineWidth = this.check( parameters.lineWidth, 1 );

        this.map = this.check( parameters.map, null );

        this.useMap = this.check( parameters.useMap, 0 );

        this.alphaMap = this.check( parameters.alphaMap, null );

        this.useAlphaMap = this.check( parameters.useAlphaMap, 0 );

        this.color = this.check( parameters.color, new THREE.Color( 0xffffff ) );

        this.opacity = this.check( parameters.opacity, 1 );

        this.resolution = this.check( parameters.resolution, new THREE.Vector2( 1, 1 ) );

        this.sizeAttenuation = this.check( parameters.sizeAttenuation, 1 );

        this.near = this.check( parameters.near, 1 );

        this.far = this.check( parameters.far, 1 );

        this.dashArray = this.check( parameters.dashArray, 0 );

        this.dashOffset = this.check( parameters.dashOffset, 0 );

        this.dashRatio = this.check( parameters.dashRatio, 0.5 );

        this.useDash = ( this.dashArray !== 0 ) ? 1 : 0;

        this.visibility = this.check( parameters.visibility, 1 );

        this.alphaTest = this.check( parameters.alphaTest, 0 );

        this.repeat = this.check( parameters.repeat, new THREE.Vector2( 1, 1 ) );

 

        let material = new THREE.RawShaderMaterial( {

            uniforms:{

                lineWidth: { type: 'f', value: this.lineWidth },

                map: { type: 't', value: this.map },

                useMap: { type: 'f', value: this.useMap },

                alphaMap: { type: 't', value: this.alphaMap },

                useAlphaMap: { type: 'f', value: this.useAlphaMap },

                color: { type: 'c', value: this.color },

                opacity: { type: 'f', value: this.opacity },

                resolution: { type: 'v2', value: this.resolution },

                sizeAttenuation: { type: 'f', value: this.sizeAttenuation },

                near: { type: 'f', value: this.near },

                far: { type: 'f', value: this.far },

                dashArray: { type: 'f', value: this.dashArray },

                dashOffset: { type: 'f', value: this.dashOffset },

                dashRatio: { type: 'f', value: this.dashRatio },

                useDash: { type: 'f', value: this.useDash },

                visibility: {type: 'f', value: this.visibility},

                alphaTest: {type: 'f', value: this.alphaTest},

                repeat: { type: 'v2', value: this.repeat }

            },

            vertexShader: vertexShaderSource.join( '\r\n' ),

            fragmentShader: fragmentShaderSource.join( '\r\n' )

        });

 

        delete parameters.lineWidth;

        delete parameters.map;

        delete parameters.useMap;

        delete parameters.alphaMap;

        delete parameters.useAlphaMap;

        delete parameters.color;

        delete parameters.opacity;

        delete parameters.resolution;

        delete parameters.sizeAttenuation;

        delete parameters.near;

        delete parameters.far;

        delete parameters.dashArray;

        delete parameters.dashOffset;

        delete parameters.dashRatio;

        delete parameters.visibility;

        delete parameters.alphaTest;

        delete parameters.repeat;

 

        material.type = 'MeshLineMaterial';

        material.setValues( parameters );

 

        return material;

    }

 

    check( v, d )

    {

        if( v === undefined ) return d;

        return v;

    }

 

    copy( source )

    {

        super.copy( this, source );

 

        this.lineWidth = source.lineWidth;

        this.map = source.map;

        this.useMap = source.useMap;

        this.alphaMap = source.alphaMap;

        this.useAlphaMap = source.useAlphaMap;

        this.color.copy( source.color );

        this.opacity = source.opacity;

        this.resolution.copy( source.resolution );

        this.sizeAttenuation = source.sizeAttenuation;

        this.near = source.near;

        this.far = source.far;

        this.dashArray.copy( source.dashArray );

        this.dashOffset.copy( source.dashOffset );

        this.dashRatio.copy( source.dashRatio );

        this.useDash = source.useDash;

        this.visibility = source.visibility;

        this.alphaTest = source.alphaTest;

        this.repeat.copy( source.repeat );

 

        return this;

    }

}

 

 

 

위에 JS파일을 import 하여 아래 코드예제참고하여 같이 써주면 된다.

사용예제) [모듈화로 이름을 모듈('module'을 붙여 바꾸었다) ]

 

  import * as THREELINE  from "../lib/THREE.MeshLine.module.js";

 

class CTest

{

  //코드들...

  //..

 

TestLine()

{

    //코드들...

    //..

 

   var geometry= new THREE.Geometry();

        geometry.vertices.push(

            new THREE.Vector3( -0.5, 0.5, 0 ),

            new THREE.Vector3( 0.5, 0.5, 0 ),

            new THREE.Vector3( 0.5, -0.5, 0 ),

            new THREE.Vector3( -0.5, -0.5, 0 ),

            new THREE.Vector3( -0.5, 0.5, 0 ),

        );

 

   let gFig = new THREELINE.MeshLineModule();

 

   gFig.setGeometry( geometry);

 

   let obj3DMeBG = new THREE.Object3D();

   let resolution = new THREE.Vector2( 800, 600); //화면비율 width, height

 

   let opt = {

                color: new THREE.Color( 0xff0000 ), //컬러 부분은 THREE.Color 써야한다.

                opacity: 1,

                resolution: resolution,

                lineWidth: 1/1000, //화면대비 비율로 굵기가 정해진다. (대충 1000으로 정함)

            };

 

   var material = new THREELINE.MeshLineMaterialModule(OPTIONS);

 

   let meshFig = new THREE.Mesh(gFig.geometry, material );

   meshFig.position.set(0.0, 0.0, 0.0);

   obj3DMeBG .add( meshFig );

 

}

 

}

 

 

 

=================================

=================================

=================================

 

 

반응형
그리드형


관련글 더보기

댓글 영역