【问题标题】:Applying glow effect to a square using glsl without texture使用没有纹理的 glsl 将发光效果应用于正方形
【发布时间】:2015-05-17 00:21:41
【问题描述】:

我从 Android OpenGL 教程中获取了一些相同的代码,我想知道是否有可能实现这里看到的发光效果:

http://glslsandbox.com/e#25224.0

使用下面的Square 实现?即不使用纹理?我想将这种发光效果应用于整个Square,即填充

上面的链接使用了一个resolution 变量,我不确定如果我想对我的形状施加影响是否需要这个。我假设不需要time 变量?

我在网上看到了很多片段着色器用于产生发光效果的示例,但大多数都使用纹理。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import android.opengl.GLES20;

/**
 * A two-dimensional square for use as a drawn object in OpenGL ES 2.0.
 */
public class Square {

    private final String vertexShaderCode =
            "uniform mat4 uMVPMatrix;" +
            "attribute vec4 vPosition;" +
            "void main() {" +
            "  gl_Position = uMVPMatrix * vPosition;" +
            "}";

    private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +
            "void main() {" +
            "  gl_FragColor = vColor;" +
            "}";

    private final FloatBuffer vertexBuffer;
    private final ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mColorHandle;
    private int mMVPMatrixHandle;

    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 3;
    static float squareCoords[] = {
            -0.5f,  0.5f, 0.0f,   // top left
            -0.5f, -0.5f, 0.0f,   // bottom left
             0.5f, -0.5f, 0.0f,   // bottom right
             0.5f,  0.5f, 0.0f }; // top right

    private final short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
    float color[] = { 0.2f, 0.709803922f, 0.898039216f, 1.0f };

    /**
     * Sets up the drawing object data for use in an OpenGL ES context.
     */
    public Square() {

        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        // prepare shaders and OpenGL program
        int vertexShader = MyGLRenderer.loadShader(
                GLES20.GL_VERTEX_SHADER,
                vertexShaderCode);
        int fragmentShader = MyGLRenderer.loadShader(
                GLES20.GL_FRAGMENT_SHADER,
                fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
    }

    /**
     * Encapsulates the OpenGL ES instructions for drawing this shape.
     *
     * @param mvpMatrix - The Model View Project matrix in which to draw
     * this shape.
     */
    public void draw(float[] mvpMatrix) {

        GLES20.glUseProgram(mProgram);

        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        GLES20.glVertexAttribPointer(
                mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);

        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);

        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
        MyGLRenderer.checkGlError("glGetUniformLocation");

        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
        MyGLRenderer.checkGlError("glUniformMatrix4fv");

        GLES20.glDrawElements(
                GLES20.GL_TRIANGLES, drawOrder.length,
                GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}

【问题讨论】:

    标签: opengl glsl glow


    【解决方案1】:

    resolution 变量被使用的唯一原因只是为了得到一个有效的 uv 映射。通常,我建议您将纹理坐标(uv 映射)添加到您的Square。您不必使用纹理,只需使用纹理坐标。 在这种情况下,您的片段着色器将是:

    uniform float u_time;
    varying vec2 v_uv;
    
    void main( void ) {
        vec2 uv = v_uv;
        // Zooms out by a factor of 2.0
        uv *= 2.0;
        // Shifts every axis by -1.0
        uv -= 1.0;
    
        // Base color for the effect
        vec3 finalColor = vec3 ( .2, 1., 0. );
    
        finalColor *= abs(0.05 / (sin( uv.x + sin(uv.y+u_time)* 0.3 ) * 20.0) );
    
        gl_FragColor = vec4( finalColor, 1.0 );    
    }
    

    在顶点着色器中,您需要将 uv 坐标传递给片段着色器:

    attribute vec4 vPosition;
    attribute vec4 uv;
    uniform mat4 uMVPMatrix;
    varying vec2 v_uv;
    
    void main() 
    {
        v_uv = uv;
        gl_Position = uMVPMatrix * vPosition;
    }
    

    您还必须为 uv 坐标再创建一个顶点缓冲区,或者将 uv 坐标打包到现有缓冲区中。 然后,您将需要执行您对顶点属性 vPosition 执行的所有操作,也需要对新的 uv 属性执行所有操作。我的意思是,您需要为 uv 属性执行 glGetAttribLocationglEnableVertexAttribArrayglVertexAttribPointer

    Here 是一个教程,可能会对你有所帮助。

    我用threejs写了一个小例子:

       var container;
       var camera, scene, renderer;
       var mesh;
       var uniforms;
    
       var clock = new THREE.Clock();
    
       init();
       animate();
    
       function init() {
         container = document.getElementById('container');
    
         camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 3000);
         camera.position.z = 2.0;
         camera.position.y = 1.0;
         camera.rotation.x = -0.45;
    
         scene = new THREE.Scene();
    
         var boxGeometry = new THREE.CubeGeometry(0.75, 0.75, 0.75);
    
         uniforms = {u_time: {type: "f", value: 0.0 } };
    
         var material = new THREE.ShaderMaterial({
           uniforms: uniforms,
           vertexShader: document.getElementById('vertexShader').textContent,
           fragmentShader: document.getElementById('fragment_shader').textContent
         });
    
         mesh = new THREE.Mesh(boxGeometry, material);
         scene.add(mesh);
    
         renderer = new THREE.WebGLRenderer();
         renderer.setClearColor( 0xffffff, 1 );
         container.appendChild(renderer.domElement);
    
         onWindowResize();
    
         window.addEventListener('resize', onWindowResize, false);
    
       }
    
       function onWindowResize(event) {
         camera.aspect = window.innerWidth / window.innerHeight;
         camera.updateProjectionMatrix();
         renderer.setSize(window.innerWidth, window.innerHeight);
       }
    
       function animate() {
         requestAnimationFrame(animate);
         render();
       }
    
       function render() {
         var delta = clock.getDelta();
         uniforms.u_time.value += delta;
         mesh.rotation.y += delta * 0.5;
         renderer.render(scene, camera);
       }
    body { margin: 0px; overflow: hidden; }
    <script src="http://threejs.org/build/three.min.js"></script>
    <div id="container"></div>
    
    <script id="fragment_shader" type="x-shader/x-fragment">
        uniform float u_time;
    	varying vec2 v_uv;
        
        void main( void ) {
            vec2 uv = v_uv;
            // Zooms out by a factor of 2.0
            uv *= 2.0;
            // Shifts every axis by -1.0
            uv -= 1.0;
            
            // Base color for the effect
            vec3 finalColor = vec3 ( .2, 1., 0. );
            
            finalColor *= abs(0.05 / (sin( uv.x + sin(uv.y+u_time)* 0.3 ) * 20.0) );
        
            gl_FragColor = vec4( finalColor, 1.0 );    
        }
    </script>
    
    <script id="vertexShader" type="x-shader/x-vertex">
        varying vec2 v_uv;
                    
        void main()
        {
    		v_uv = uv;
    		vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    		gl_Position = projectionMatrix * mvPosition;
        }
    </script>

    作为替代方案,您可以根本不修改您的 java 代码,只需在顶点着色器中从正方形的对象空间顶点坐标计算 uv 坐标,然后将它们传递给片段着色器。

    顶点着色器:

    attribute vec4 vPosition;
    uniform mat4 uMVPMatrix;
    varying vec2 v_uv;
    
    void main() 
    {
        v_uv = vPosition.xy + vec2(0.5);      //this expression depends on the actual vertex coordinates values.
        gl_Position = uMVPMatrix * vPosition;
    }
    

    片段着色器将是相同的。

    更新

    我想,你想要在你的广场上完全一样的线。如果你只想要一些发光效果而不使用纹理,你可以使用距离场。 对于矩形,距离场可以简单计算为:

    float distanceField = length(max(abs(uv)-rectangleSize,0.0));
    

    其中rectangleSize在uv映射中矩形的大小,uv兴趣点的uv坐标。 要将距离场映射为: 0.0 - 点在矩形内,1.0 点在边框的远端。 你可以做以下事情:

    float distanceField = length(max(abs(uv)-rectangleSize,0.0) / borderSize);
    

    其中 borderSize 在 uv 映射中的边框大小。

    因此,您的最终片段着色器将是:

    varying vec2 v_uv;
    
    void main( void ) {
        vec2 uv = v_uv;
        // Zooms out by a factor of 2.0
        uv *= 2.0;
        // Shifts every axis by -1.0
        uv -= 1.0;
    
        // Base color for the effect
        vec3 color = vec3 ( .2, 1., 0. );
    
        // specify size of border. 0.0 - no border, 1.0 - border occupies the entire space
        vec2 borderSize = vec2(0.3); 
    
        // size of rectangle in terms of uv 
        vec2 rectangleSize = vec2(1.0) - borderSize; 
    
        // distance field, 0.0 - point is inside rectangle, 1.0 point is on the far edge of the border.
        float distanceField = length(max(abs(uv)-rectangleSize,0.0) / borderSize);
    
        // calculate alpha accordingly to the value of the distance field
        float alpha = 1.0 - distanceField;
    
        gl_FragColor = vec4(color, alpha);    
    }
    

    这是一个例子:

     var container;
       var camera, scene, renderer;
       var mesh;
       var uniforms;
    
       var clock = new THREE.Clock();
    
       init();
       animate();
    
       function init() {
         container = document.getElementById('container');
    
         camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 3000);
         camera.position.z = 2.0;
         camera.position.y = 1.0;
         camera.rotation.x = -0.45;
    
         scene = new THREE.Scene();
    
         var boxGeometry = new THREE.PlaneGeometry(0.75, 0.75, 1);
    
         uniforms = {u_time: {type: "f", value: 0.0 } };
    
         var material = new THREE.ShaderMaterial({
           uniforms: uniforms,
           side: THREE.DoubleSide, 
           transparent: true,
           vertexShader: document.getElementById('vertexShader').textContent,
           fragmentShader: document.getElementById('fragment_shader').textContent
         });
    
         mesh = new THREE.Mesh(boxGeometry, material);
         scene.add(mesh);
    
         renderer = new THREE.WebGLRenderer();
         renderer.setClearColor( 0xffffff, 1 );
         container.appendChild(renderer.domElement);
    
         onWindowResize();
    
         window.addEventListener('resize', onWindowResize, false);
    
       }
    
       function onWindowResize(event) {
         camera.aspect = window.innerWidth / window.innerHeight;
         camera.updateProjectionMatrix();
         renderer.setSize(window.innerWidth, window.innerHeight);
       }
    
       function animate() {
         requestAnimationFrame(animate);
         render();
       }
    
       function render() {
         var delta = clock.getDelta();
         uniforms.u_time.value += delta;
         mesh.rotation.y += delta * 0.5;
         renderer.render(scene, camera);
       }
    body { margin: 0px; overflow: hidden; }
    <script src="http://threejs.org/build/three.min.js"></script>
    <div id="container"></div>
    
    <script id="fragment_shader" type="x-shader/x-fragment">
    	varying vec2 v_uv;
        
        void main( void ) {
            vec2 uv = v_uv;
            // Zooms out by a factor of 2.0
            uv *= 2.0;
            // Shifts every axis by -1.0
            uv -= 1.0;
            
            // Base color for the effect
            vec3 color = vec3 ( .2, 1., 0. );
        
            // specify size of border. 0.0 - no border, 1.0 - border occupies the entire space
            vec2 borderSize = vec2(0.3); 
        
            // size of rectangle in terms of uv 
            vec2 rectangleSize = vec2(1.0) - borderSize; 
        
            // distance field, 0.0 - point is inside rectangle, 1.0 point is on the far edge of the border.
            float distanceField = length(max(abs(uv)-rectangleSize,0.0) / borderSize);
            
            // calculate alpha accordingly to the value of the distance field
            float alpha = 1.0 - distanceField;
        
            gl_FragColor = vec4(color, alpha);    
        }
    </script>
    
    <script id="vertexShader" type="x-shader/x-vertex">
        varying vec2 v_uv;
                    
        void main()
        {
    		v_uv = uv;
    		vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    		gl_Position = projectionMatrix * mvPosition;
        }
    </script>

    【讨论】:

    • 感谢您的回复!如果我误解了,请纠正我,但这只会将发光效果应用于Square 上的一行?我正在寻找覆盖整个Square的发光效果@
    • @bobbyrne01,是的,我以为你想要在你的广场上有这条发光的线:)。我已经更新了我的答案,以展示如何实现简单的发光效果。
    猜你喜欢
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 1970-01-01
    • 2011-08-16
    • 2017-11-19
    • 2019-04-03
    • 1970-01-01
    相关资源
    最近更新 更多