【问题标题】:Is there a way to draw a circle with the fragment shader at the position of a point from the vertex shader?有没有办法用片段着色器在顶点着色器的一个点的位置画一个圆?
【发布时间】:2019-01-17 13:05:18
【问题描述】:

所以我在屏幕的某个……嗯……点画一个点,然后移动。我的顶点着色器看起来像这样。

private final String vertexShaderCode =
     "attribute vec4 vPosition;" +
                "uniform mat4 Projection; \n" +
                "uniform mat4 ModelView; \n" +
                "void main() {" +
                        "  gl_Position = Projection * ModelView * vPosition;" +
                        "  gl_PointSize = 900.0; " +
                        "}"; 

我想要实现的是然后围绕这个顶点的位置渲染一个圆圈。为此,我正在使用片段着色器:

 private final String fragmentShaderCode =
        "precision highp float; \n" +
                "uniform vec2 aCirclePosition; \n" +
                "uniform float aRadius; \n" +
                "uniform vec4 aColor; \n" +
                "const float threshold = 0.3;\n" +
                "void main() \n" +
                "{ \n" +
                "   float d, dist;\n" +
                "   dist = distance(aCirclePosition, gl_FragCoord.xy);\n" +
                "   if(dist == 0.)\n" +
                "       dist = 1.;\n" +
                "   d = aRadius / dist;\n" +
                "   if(d >= 1.)\n" +
                "        gl_FragColor = aColor;\n" +
                "   else if(d >= 1. - threshold) \n" +
                "   {\n" +
                "        float a = (d - (1. - threshold)) / threshold;\n" +
                "        gl_FragColor = vec4(0., 0., 0., 1.); \n" +
                "    }\n" +
                "    else\n" +
                "        gl_FragColor = vec4(aColor.r, aColor.g, aColor.b, 0.);\n" +
                "} \n";

如本例所示:https://gist.github.com/beetsolutions/9c343f86ec44987de4550cada118e560

但是,这样绘制的圆将始终保持在屏幕上的静态位置,而与我使用的一个顶点的实际位置无关。 有什么方法可以将这两个职位联系起来或以任何其他方式实现我想要做的事情?

我的想法是在顶点着色器中使用一个“可变”变量来保持位置,但是这似乎不起作用,因为它们使用不同的空间,(我相信顶点着色器中的 gl_position 在剪辑中空间,而片段着色器使用实际的屏幕坐标,但我不确定那里),我不知道如何转换它们。

有人可以帮助我或为我指出解决该问题的正确方向吗?我真的刚开始使用openGL,所以请告诉我这里是否有一些基本的东西。

【问题讨论】:

    标签: android opengl-es glsl shader


    【解决方案1】:

    计算顶点着色器中点的窗口坐标。为此,您必须知道视口的大小 (uResolution)。将点的位置传递给片段着色器(pointPos):

    precision mediump float;
    
    attribute vec4 vPosition;
    varying   vec2 pointPos;
    uniform   vec2 uResolution; // = (window-width, window-height)
    uniform   mat4 Projection;
    uniform   mat4 ModelView;
    
    void main()
    {
        gl_Position  = Projection * ModelView * vPosition;
        gl_PointSize = 900.0;
    
        vec2 ndcPos = gl_Position.xy / gl_Position.w;
        pointPos    = uResolution * (ndcPos*0.5 + 0.5);
    }
    

    在片段着色器中使用点的位置 (pointPos) 而不是统一的aCirclePosition

    如果要将点重新设置为圆形区域,则可以使用discard 关键字来阻止片段着色器输出:

    precision highp float;
    
    varying vec2  pointPos;
    uniform float aRadius;
    uniform vec4  aColor;
    
    const float threshold = 0.3;
    
    void main()
    {
        float dist = distance(pointPos, gl_FragCoord.xy);
        if (dist > aRadius)
            discard;
    
        float d = dist / aRadius;
        vec3 color = mix(aColor.rgb, vec3(0.0), step(1.0-threshold, d));
    
        gl_FragColor = vec4(color, 1.0);
    }
    

    请注意,点图元的varying 变量不会被插值,因为varying 变量会在被渲染的图元上插值,但点只有一个坐标。

    OpenGL ES 2.0 Full Specification; 3.3 Points; page 51:

    [...] 栅格化一个点产生的所有片段都被分配了相同的关联数据,即与该点对应的顶点的数据。


    请参阅以下 WebGL 示例,其中我使用了答案中的着色器:

    var gl;
    var prog;
    var bufObj = {};
    var ShaderProgram = {};        
    
    function renderScene(){
    
        var canvas = document.getElementById( "ogl-canvas" );
        var vp = [canvas.width, canvas.height];
        
        gl.viewport( 0, 0, canvas.width, canvas.height );
        gl.enable( gl.DEPTH_TEST );
        gl.clearColor( 0.0, 0.0, 1.0, 1.0 );
        gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
        
        ShaderProgram.Use( progDraw );
        ShaderProgram.SetUniformF1( progDraw, "aRadius", 50 )
        ShaderProgram.SetUniformF2( progDraw, "uResolution", [canvas.width, canvas.height] )
        ShaderProgram.SetUniformF4( progDraw, "aColor", [1.0, 1.0, 0.0, 1.0] )
        
        gl.enableVertexAttribArray( progDraw.inPos );
        gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
        gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 ); 
        gl.drawArrays( gl.POINTS, 0, 5 );
        gl.disableVertexAttribArray( progDraw.pos );
    
        requestAnimationFrame(renderScene);
    }  
    
    function initScene() {
    
        var canvas = document.getElementById( "ogl-canvas");
        gl = canvas.getContext( "experimental-webgl" );
        if ( !gl )
          return;
    
        progDraw = ShaderProgram.Create( 
          [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
            { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
          ] );
        progDraw.inPos = gl.getAttribLocation( progDraw, "vPosition" );
        if ( prog == 0 )
            return;
    
        var pos = [ 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0 ];
        bufObj.pos = gl.createBuffer();
        gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
        gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
    }
    
    ShaderProgram.Create = function( shaderList ) {
        var shaderObjs = [];
        for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
            var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
            if ( shderObj == 0 )
                return 0;
            shaderObjs.push( shderObj );
        }
        var progObj = this.LinkProgram( shaderObjs )
        if ( progObj != 0 ) {
            progObj.attribIndex = {};
            var noOfAttributes = gl.getProgramParameter( progObj, gl.ACTIVE_ATTRIBUTES );
            for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
                var name = gl.getActiveAttrib( progObj, i_n ).name;
                progObj.attribIndex[name] = gl.getAttribLocation( progObj, name );
            }
            progObj.unifomLocation = {};
            var noOfUniforms = gl.getProgramParameter( progObj, gl.ACTIVE_UNIFORMS );
            for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
                var name = gl.getActiveUniform( progObj, i_n ).name;
                progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
            }
        }
        return progObj;
    }
    ShaderProgram.AttributeIndex = function( progObj, name ) { return progObj.attribIndex[name]; } 
    ShaderProgram.UniformLocation = function( progObj, name ) { return progObj.unifomLocation[name]; } 
    ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); } 
    ShaderProgram.SetUniformI1  = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1i( progObj.unifomLocation[name], val ); }
    ShaderProgram.SetUniformF1  = function( progObj, name, val ) { if(progObj.unifomLocation[name]) gl.uniform1f( progObj.unifomLocation[name], val ); }
    ShaderProgram.SetUniformF2  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform2fv( progObj.unifomLocation[name], arr ); }
    ShaderProgram.SetUniformF3  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform3fv( progObj.unifomLocation[name], arr ); }
    ShaderProgram.SetUniformF4  = function( progObj, name, arr ) { if(progObj.unifomLocation[name]) gl.uniform4fv( progObj.unifomLocation[name], arr ); }
    ShaderProgram.SetUniformM33 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix3fv( progObj.unifomLocation[name], false, mat ); }
    ShaderProgram.SetUniformM44 = function( progObj, name, mat ) { if(progObj.unifomLocation[name]) gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
    ShaderProgram.CompileShader = function( source, shaderStage ) {
        var shaderScript = document.getElementById(source);
        if (shaderScript) {
          source = "";
          var node = shaderScript.firstChild;
          while (node) {
            if (node.nodeType == 3) source += node.textContent;
            node = node.nextSibling;
          }
        }
        var shaderObj = gl.createShader( shaderStage );
        gl.shaderSource( shaderObj, source );
        gl.compileShader( shaderObj );
        var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
        if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
        return status ? shaderObj : 0;
    } 
    ShaderProgram.LinkProgram = function( shaderObjs ) {
        var prog = gl.createProgram();
        for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
            gl.attachShader( prog, shaderObjs[i_sh] );
        gl.linkProgram( prog );
        status = gl.getProgramParameter( prog, gl.LINK_STATUS );
        if ( !status ) alert("Could not initialise shaders");
        gl.useProgram( null );
        return status ? prog : 0;
    }
    
    initScene();
    renderScene();
    <script id="draw-shader-vs" type="x-shader/x-vertex">
    precision mediump float;
    
    attribute vec4 vPosition;
    
    varying vec2 pointPos;
    
    uniform vec2 uResolution;
    
    void main()
    {
        gl_PointSize = 100.0;
        gl_Position  = vPosition;
        
        vec2 ndcPos = gl_Position.xy / gl_Position.w;
        pointPos = uResolution * (ndcPos*0.5 + 0.5);
    }
    </script>
      
    <script id="draw-shader-fs" type="x-shader/x-fragment">
    precision highp float;
    
    varying vec2  pointPos;
    uniform float aRadius;
    uniform vec4  aColor;
    
    const float threshold = 0.3;
    
    void main()
    {
        float dist = distance(pointPos, gl_FragCoord.xy);
        if (dist > aRadius)
            discard;
    
        float d = dist / aRadius;
        vec3 color = mix(aColor.rgb, vec3(0.0), step(1.0-threshold, d));
    
        gl_FragColor = vec4(color, 1.0);
    }
    </script>
    
    <canvas id="ogl-canvas" style="border: none;" width="256" height="256"></canvas>

    【讨论】:

    • 非常感谢,这正是我所需要的。
    猜你喜欢
    • 1970-01-01
    • 2015-11-20
    • 1970-01-01
    • 2019-01-24
    • 2011-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多