Сопоставьте два цвета с двумя другими цветами в GLSL

#glsl #webgl

#glsl #webgl

Вопрос:

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

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

Комментарии:

1. Просто используйте mix (color0,color1,noise)

Ответ №1:

Есть простые способы

  1. Негибкий способ, используйте mix
     gl_FragColor = mix(color0, color1, noise)
     
  2. Более гибкий способ — использовать рамповую текстуру
     float u = (noise * (rampTextureWidth - 1.0)   0.5) / rampTextureWidth;
    gl_FragColor = texture2D(rampTexture, vec2(u, 0.5));
     

Использование текстур рампы обрабатывает любое количество цветов, в то время как as mix обрабатывает только 2.

 const vs = `
attribute vec4 position;
attribute float noise;

uniform mat4 u_matrix;

varying float v_noise;

void main() {
   gl_Position = u_matrix * position;
   v_noise = noise;
}
`;
const fs = `
precision highp float;

varying float v_noise;

uniform sampler2D rampTexture;
uniform float rampTextureWidth;

void main() {
  float u = (v_noise * (rampTextureWidth - 1.0)   0.5) / rampTextureWidth;
  gl_FragColor = texture2D(rampTexture, vec2(u, 0.5));
}
`;

"use strict";
const m4 = twgl.m4;
const gl = document.querySelector("canvas").getContext("webgl");
// compiles shaders, links program, looks up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

/*
       6------7
      /|     /|
     / |    / |
    2------3  |
    |  |   |  |
    |  4---|--5
    | /    | /
    |/     |/
    0------1
*/

const arrays = {
  position: [
    -1, -1, -1,
     1, -1, -1,
    -1,  1, -1,
     1,  1, -1,
    -1, -1,  1,
     1, -1,  1,
    -1,  1,  1,
     1,  1,  1,
  ], 
  noise: {
    numComponents: 1,
    data: [
      1, 0.5, 0.2, 0.3, 0.9, 0.1, 0.7, 1,
    ],
  },
  indices: [
    0, 2, 1, 1, 2, 3,
    1, 3, 5, 5, 3, 7,
    5, 7, 4, 4, 7, 6,
    4, 6, 0, 0, 6, 2,
    2, 6, 3, 6, 7, 3,
    0, 1, 4, 4, 1, 5,
  ],
};
const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

const red     = [255,   0,   0, 255];
const yellow  = [255, 255,   0, 255];
const blue    = [  0,   0, 255, 255];
const green   = [  0, 255,   0, 255];
const cyan    = [  0, 255, 255, 255];
const magenta = [255,   0, 255, 255];

function makeTexture(gl, name, colors) {
  const width = colors.length / 4;
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
     width, 1, 0,
     gl.RGBA, gl.UNSIGNED_BYTE,
     new Uint8Array(colors));
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  return {
    name,
    texture,
    width,
  };
}

const textures = [
  makeTexture(gl, 'one color',
    [...red]),
  makeTexture(gl, 'two colors',
    [...red, ...yellow]),
  makeTexture(gl, 'three colors',
    [...blue, ...red, ...yellow]),
  makeTexture(gl, 'six colors',
    [...green, ...red, ...blue, ...yellow, ...cyan, ...magenta]),
];

const infoElem = document.querySelector('#info');

function render(time) {
  time *= 0.001;

  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);

  // draw cube
  const fov = 30 * Math.PI / 180;
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  const zNear = 0.5;
  const zFar = 40;
  const projection = m4.perspective(fov, aspect, zNear, zFar);
  const eye = [1, 4, -7];
  const target = [0, 0, 0];
  const up = [0, 1, 0];

  const camera = m4.lookAt(eye, target, up);
  const view = m4.inverse(camera);
  const viewProjection = m4.multiply(projection, view);
  const world = m4.rotationY(time);

  gl.useProgram(programInfo.program);
  
  const tex = textures[time / 2 % textures.length | 0];
  infoElem.textContent = tex.name;
    
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  // calls gl.uniformXXX, gl.activeTexture, gl.bindTexture
  twgl.setUniformsAndBindTextures(programInfo, {
    u_matrix: m4.multiply(viewProjection, world),
    rampTexture: tex.texture,
    rampTextureWidth: tex.width,
  });
  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render); 
 <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
<div id="info"></div>