有没有办法在片段着色器上顶点着色器的一点处绘制一个圆? [英] Is there a way to draw a circle with the fragment shader at the position of a point from the vertex shader?

查看:87
本文介绍了有没有办法在片段着色器上顶点着色器的一点处绘制一个圆?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我在屏幕的某个特定位置绘制了一个点,然后在该点附近移动.我的顶点着色器看起来像这样.

So i am drawing a Point at a certain ... well ... point of the screen that is then moving around. My Vertex Shader looks like this.

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; " +
                        "}"; 

我要达到的目的是在这个顶点的位置周围绘制一个圆.为此,我正在使用片段着色器:

What i am trying to achive is to then render a circle around the position of this one vertice. For that i am using the fragment shader:

 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

as shown in this example: https://gist.github.com/beetsolutions/9c343f86ec44987de4550cada118e560

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

However, with this the drawn circle will always remain at a static position on the screen independent of the actual position of the one vertice i use. Is there any way to link these two positions or achive what i am trying to do in any other way?

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

My idea was to use a "varying" variable in the Vertice Shader which will hold the position, however it seems like that does not work, since they use different spaces, (i believe the gl_position in the vertex shader is in clip space while the fragment shader uses actual screen coordinates but im not sure there), and i have no clue on how i would transform them.

有人可以帮助我或为我指出解决该问题的正确方向吗?我真的才刚开始使用openGL,所以请告诉我这里是否存在根本性的困扰.

Could someone help me or point me in the right direction to solving that problem? I really just started working with openGL so please tell me if there is something fundemental im mising here.

推荐答案

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

Calculate the window coordinate of the point in the vertex shader. For thsi you have to know the size of the viewport (uResolution). Pass the position of the point to the fragment shader (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 .

Use the position of the point (pointPos) instead of the uniform aCirclePosition in the fragment shader.

如果要将点恢复到圆形区域,则可以使用 discard 关键字可防止片段着色器输出:

If you want to restirct the point to a circular area, then you can use the discard keyword to prevent fragment shader outputs:

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 变量,但是一个点只有一个坐标.


Note the varying variables of a point primitive are not interpolated, because the varying variable are interpolated over the primitive being rendered, but a point has only a single coordinate.

请参见 OpenGL ES 2.0完整规范;3.3分;第51 :

[...]栅格化一个点时产生的所有片段都分配有相同的关联数据,这些数据是与该点相对应的顶点的数据.

[...] All fragments produced in rasterizing a point are assigned the same associated data, which are those of the vertex corresponding to the point.


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


See the following WebGL example, where I used the shader from the answer:

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>

这篇关于有没有办法在片段着色器上顶点着色器的一点处绘制一个圆?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆