WebGL - Как нарисовать точки в фрагментном шейдере с цветовой интерполяцией, основанной на значении оценки для каждой точки?

1

У меня есть несколько точек в двухмерных координатах, и у каждой точки есть оценка. Я хочу нарисовать точки на шейдере, и цвет точек должен быть красным, но вес должен быть основан на счете.

Это должно выглядеть следующим образом, например, верхний левый балл равен 0,8, верхний правый и нижний левый балл равен 0,9, а нижний правый балл равен 0,7.

(Приношу извинения за то, что не смог вставить некоторые изображения сюда. Это мой первый вопрос, и у меня не хватает репутации....)

https://jsfiddle.net/p19uc825/

Формула, которую я сейчас имею, рассчитывает расстояние между центром и 2-й координатой для каждой точки. Ниже приведен метод, который я использую, чтобы получить цвет для каждой позиции.

Мои координаты:

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 близкие к ней точки, верхнюю правую и нижнюю правую. 2. Новая точка может быть добавлена с большим весом, потому что она находится ближе всего к центру, потому что в коде фрагмента шейдера выше, я вычисляю расстояние между точкой и центром.

Чего я действительно хочу добиться, так это того, чтобы вес цвета для каждой точки определялся только оценкой, независимо от ее положения.

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

Теги:
graphics
shader
webgl
fragment-shader

1 ответ

0

Я рекомендую рассчитать минимальное расстояние до точки, где "сокр" взвешивает обратное расстояние (1/х):

float min_pct = 100.0; // any large value
for(int i=0;i<6;i++){
    float sco = uScores[i];
    vec2 pos = uPositions[i]; 

    float mult = 2.5;
    float pct = distance(st.xy, pos.xy) * mult / sco;

    min_pct = min(min_pct, pct);
}

Используйте mix чтобы интерполировать между 2 цветами в соответствии с окончательным весом

vec3 color1 = vec3(1.0);
vec3 color2 = vec3(1.0, 0.0, 0.0);
result = mix(color1, color2, 1.0-min_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;
							vec3 result = vec3(1,1,1);
              
              float min_pct = 100.0; // any large value
              for(int i=0;i<6;i++){
                  float sco = uScores[i];
                  vec2 pos = uPositions[i]; 
                  
                  float mult = 2.5;
                  float pct = distance(st.xy, pos.xy) * mult / sco;
                  
                  min_pct = min(min_pct, pct);
							}
              
              vec3 color1 = vec3(1.0);
              vec3 color2 = vec3(1.0, 0.0, 0.0);
              result = mix(color1, color2, 1.0-min_pct);
              
              result = result + vec3(1,0,0);
              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 already bound, but that 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>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню