Есть ли способ нарисовать круг с фрагментным шейдером в положении точки из вершинного шейдера? - PullRequest
0 голосов
/ 17 января 2019

Итак, я рисую точку в определенной ... ну ... точке экрана, которая затем перемещается. Мой Vertex Shader выглядит следующим образом.

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

Однако при этом нарисованный круг всегда будет оставаться в статическом положении на экране независимо от фактического положения одной вершины, которую я использую. Есть ли способ связать эти две позиции или добиться того, что я пытаюсь сделать каким-либо другим способом?

Моя идея заключалась в том, чтобы использовать переменную переменную в шейдере Vertice, которая будет удерживать позицию, однако, похоже, что это не работает, поскольку они используют разные пробелы (я считаю, что gl_position в вершинном шейдере находится в клипе пространство, в то время как фрагментный шейдер использует фактические координаты экрана, но я не уверен там), и я понятия не имею, как бы я их преобразовал.

Может ли кто-нибудь помочь мне или направить меня в правильном направлении к решению этой проблемы? Я действительно только начал работать с openGL, поэтому, пожалуйста, скажите мне, есть ли здесь что-то фундаментальное?

1 Ответ

0 голосов
/ 17 января 2019

Рассчитать координату окна точки в вершинном шейдере. Для этого вы должны знать размер области просмотра (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; 3,3 балла; страница 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>
...