WebGL-如何根据每个点的得分值使用颜色插值在片段着色器中绘制点?

时间:2018-12-17 23:38:18

标签: javascript graphics webgl shader fragment-shader

我在2d坐标中有几个点,每个点都有一个分数。我想在着色器上绘制点,并且点的颜色都应为红色,但是权重必须基于分数。

应该看起来像这样,例如,左上得分为0.8,右上和左下得分为0.9,右下得分为0.7。

(抱歉,无法在此处粘贴一些图像。这是我有史以来第一个提出的问题,我的信誉不高。)

https://jsfiddle.net/p19uc825/

我现在拥有的公式是计算每个点的中心和2d坐标之间的距离。下面是我用来获取每个位置的颜色的方法。

我的坐标:

const coordinates = [
    0.2,0.8,
    0.2,0.2,
    0.8,0.8,
    0.8,0.2,
];  

得分:

const scores = [
    0.8,0.9,0.9,0.7
];  

片段着色器源的一部分:

vec3 get_pos_color(in vec2 st, in vec2 normalizedPosition, in float score)
{               
    float mult = 1.4;
    float pct = 0.0;
    pct = ( 
         distance(
             vec3(normalizedPosition.x, normalizedPosition.y, score), 
             vec3(st.x,st.y,score)
         ) * mult - score);

    vec3 color = vec3(pct, pct, pct) + vec3(1,1,1);
    return color;
}    

这可以处理4个坐标,如上面的jsfiddle所示。

但是,如果我添加另一个坐标为[0.8,0.5]的点,并将其分配为0.1,看起来像这样-

https://jsfiddle.net/nqy3az1b/

似乎新的点比这两个0.9点的权重更大,而它应该具有非常浅的颜色,因为该新点的得分最低。我认为有两个原因-1.着色器对新点进行插值时,会考虑到靠近它的两个点,即右上和右下。 2.新的点可能会添加更多的权重,因为它离中心最近,如上面的片段着色器代码中的cuz所示,我正在计算点与中心之间的距离。

我真正想要实现的是,每个点的颜色权重仅由分数决定,而与位置无关。

我知道片段源中的distance()并不是用于插值像素的正确公式,因此,对我可以尝试的任何建议将不胜感激。

1 个答案:

答案 0 :(得分:0)

我建议通过指数函数计算到点的反距离,其中“分数”对结果加权(sco * pow(1.0 - distance(st.xy, pos.xy), 8.0))。 汇总分数(sum_pct += max(0.0, pct)),但忽略低于0.0的分数:

float sum_pct = 0.0;
for (int i=0; i<6; i++) {
    float sco = uScores[i];
    vec2 pos = uPositions[i]; 

    float pct = sco * pow(1.0 - distance(st.xy, pos.xy), 8.0);

    sum_pct += max(0.0, pct);
}

使用mix根据最终权重在2种颜色之间进行插值

vec3 color1 = vec3(1.0);
vec3 color2 = vec3(1.0, 0.0, 0.0);
vec3 result = mix(color1, color2, sum_pct);

请参见示例,其中我将建议应用于您的原始代码:

let glCanvas;
let glContext;
let shaderProgram = {};
let verticesBuffer;

function initialize() {

   // Get WebGL context,
   glCanvas = document.getElementById("glCanvas");
   glContext = glCanvas.getContext("webgl") || glCanvas.getContext("experimental-webgl");   
   
   if (!glContext) {
      alert("Failed to acquire a WebGL context. Sorry!");
      return false;
   }

   // Initialize shaders, buffers and state,
   if (!initializeShaderProgram()) {
      delete glContext;
      return false;
   }
   
   initializeBuffers();
   initializeState();
   

   return true;
}

function initializeShaderProgram() {

   var vertexShaderCode = 
      "attribute vec3 vertexPosition;\n" +
      "varying vec4 vertexColor;\n" +
      
      "\n" +
      "void main(void) {\n" +
      "   gl_Position = vec4(vertexPosition, 1.0);\n" +
      "}\n";
         
   var fragmentShaderCode =
   "#ifdef GL_ES\n" + 
		"precision mediump float;\n" + 
		"#endif\n" + 

      "uniform mediump float time;\n" +
      "uniform vec2 u_resolution;\n" +

			"uniform vec2 uPositions[5];\n" +
			"uniform float uScores[5];\n" +
      
      "varying lowp vec4 vertexColor;\n" +
      "\n" +
      
      `
			vec3 rgba2rgb(in vec3 RGB_background, in vec4 RGBA_color){
            float alpha = RGBA_color.a;
      
            vec3 res = vec3(
               (1.0 - alpha) * RGB_background.r + alpha * RGBA_color.r,
               (1.0 - alpha) * RGB_background.g + alpha * RGBA_color.g,
               (1.0 - alpha) * RGB_background.b + alpha * RGBA_color.b);
            return res;
      }
      
      void main(void) {
         vec2 st = gl_FragCoord.xy/u_resolution;
         
         float sum_pct = 0.0;
         for (int i=0; i<6; i++) {
            float sco = uScores[i];
            vec2 pos = uPositions[i]; 
            
            float pct = sco * pow(1.0 - distance(st.xy, pos.xy), 8.0);
            
            sum_pct += max(0.0, pct);
         }
         
         vec3 color1 = vec3(1.0);
         vec3 color2 = vec3(1.0, 0.0, 0.0);
         vec3 result = mix(color1, color2, sum_pct);
         
         gl_FragColor = vec4(result, 1.0);
      }	
      `;

   // Create shaders,
   var vertexShader   = createShader(  vertexShaderCode, glContext.  VERTEX_SHADER);
   var fragmentShader = createShader(fragmentShaderCode, glContext.FRAGMENT_SHADER);

   if ((!vertexShader) || (!fragmentShader)) return false;
   
   // Create shader program,
   shaderProgram.program = glContext.createProgram();
   glContext.attachShader(shaderProgram.program,   vertexShader);
   glContext.attachShader(shaderProgram.program, fragmentShader);
   glContext.linkProgram(shaderProgram.program);

   // Check the shader program creation status,
   if (!glContext.getProgramParameter(shaderProgram.program, glContext.LINK_STATUS)) {
      alert("Unable to initialize the shader program.");
      return false;
   }

   // Get attributes' and uniforms' locations,
   shaderProgram.attributes = {};
   shaderProgram.attributes.vertexPosition = glContext.getAttribLocation(
      shaderProgram.program, "vertexPosition");
      
   shaderProgram.uniforms = {};   
   shaderProgram.uniforms.time = glContext.getUniformLocation(shaderProgram.program, "time");
   shaderProgram.uniforms.positions = glContext.getUniformLocation(shaderProgram.program, "uPositions");
   shaderProgram.uniforms.scores = glContext.getUniformLocation(shaderProgram.program, "uScores");
      
      
   return true;
}

function createShader(shaderCode, shaderType) {

   var shader = glContext.createShader(shaderType);

   glContext.shaderSource(shader, shaderCode);
   glContext.compileShader(shader);

   if (!glContext.getShaderParameter(shader, glContext.COMPILE_STATUS)) {  
      alert("Errors occurred while compiling the shader:\n" + glContext.getShaderInfoLog(shader));
      return null;  
   }
   return shader;
}

function initializeBuffers() {

   var vertices = [
      1.0,  1.0,  0.0,
      -1.0, 1.0,  0.0,
      1.0,  -1.0, 0.0,
      -1.0, -1.0, 0.0
   ];

   verticesBuffer = glContext.createBuffer();
   glContext.bindBuffer(glContext.ARRAY_BUFFER, verticesBuffer);
   glContext.bufferData(glContext.ARRAY_BUFFER, new Float32Array(vertices), glContext.STATIC_DRAW);
}

function initializeState() {

   // Set the current shader in use,
   glContext.useProgram(shaderProgram.program);
   
 	 //tell the shader the resolution of the canvas (hard coded to 128 for simplicity)
   glContext.uniform2f(glContext.getUniformLocation(shaderProgram.program, "u_resolution"),
      document.getElementById("glCanvas").width,
      document.getElementById("glCanvas").height
   );

   // Set the vertices buffer (I know it's already bound, but that's where it normally
   // belongs in the workflow),
   glContext.bindBuffer(glContext.ARRAY_BUFFER, verticesBuffer);

   // Set where the vertexPosition attribute gets its data,
   glContext.vertexAttribPointer(shaderProgram.attributes.vertexPosition, 3, glContext.FLOAT, false, 0, 0);
   
   // Enable attributes used in shader,
   glContext.enableVertexAttribArray(shaderProgram.attributes.vertexPosition);

   // Set clear color to black,
   glContext.clearColor(0.0, 0.0, 0.0, 1.0);   
   
   
    // Set the scores
    const scores = [
    	 0.8,0.9,0.9,0.7,0.1 
    	/* 0.7,0.7,0.7,0.7 */
    ];
		glContext.uniform1fv(shaderProgram.uniforms.scores, scores);
    
    // Set the coordinates
    const coordinates = [
			0.2,0.8,
      0.2,0.2,
      0.8,0.8,
      0.8,0.2,
      0.8,0.5

    ];
		glContext.uniform2fv(shaderProgram.uniforms.positions, coordinates);

}

function start() {
   // Start the drawing loop,
   drawScene();
}

function drawScene() {
     
   // Clear the color buffer,
   glContext.clear(glContext.COLOR_BUFFER_BIT);

   // The revered draw call!
   glContext.drawArrays(glContext.TRIANGLE_STRIP, 0, 4);
   
   // Request drawing again next frame,
   window.requestAnimationFrame(drawScene);
}

// Initialize everything,
if (initialize()) { 
  // Start drawing,
  start();

}
html, body { height: 100%; width: 100%; }
.container { width: 100%; height: 100%; position: relative; }
canvas { position: absolute; width: 100%; height: 100%; background: #000; opacity: 1; z-index: 1000000; pointer-events:none; }
#map_div { position: absolute; width: 100%; height: 100%; }
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3&amp;sensor=false"></script>
<div class="container">
  <canvas id="glCanvas">
  </canvas>
  <div id="map_div"></div>
</div>