首页 > 解决方案 > WebGL2:不同着色器中的相同功能

问题描述

我有一个包含复杂数学的大函数,我想在几个片段着色器中调用这个相同的函数。我是否必须将函数的代码复制粘贴到每个着色器?或者有什么办法可以避免这种情况,在着色器之间共享代码?我可以为常用着色器功能提供任何类型的“库”吗?

标签: glslfragment-shaderwebgl2

解决方案


在 WebGL 的着色器中共享代码的方法是通过字符串操作。例子

const hsv2rgb = `

vec3 hsv2rgb(vec3 c) {
  c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
`;

const fragmentShader1 = `#version 300 es

${hsv2rgb}

in float hue;
out vec4 color;

void main() {
  color = vec4(hsv2Rgb(vec3(hue, 1.0, 0.8)), 1);
}

const fragmentShader2 = `#version 300 es

${hsv2rgb}

in vec3 hsv;
out vec4 color;

void main() {
  color = vec4(hsv2Rgb(hsv), 1);
}
`;

不需要图书馆,因为它很简单。例子

例子

const snippets = {
  hsv2rgb: `...code-from-above--...`,
  rgb2hsv: `...some code ...`,
};

现在只需使用片段

const fragmentShader2 = `#version 300 es

${snippets.hsv2rgb}
${snippets.rgb2hsv}

in vec3 v_color;
out vec4 color;

void main() {
  vec3 hsv = rgb2hsv(v_color);
  color = vec4(hsv2Rgb(hsv + vec3(0.5, 0, 0), 1);
}
`;

尽管我建议不要使用对象来收集字符串,因为您使用的任何构建器都可能无法丢弃未使用的片段。

组织你可以使用 es6 导入

/* hsv2rgb.glsl.js */
export default `

vec3 hsv2rgb(vec3 c) {
  c = vec3(c.x, clamp(c.yz, 0.0, 1.0));
  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
`; 

然后你可以导入

/* somefragshader.glsl.js */
import hsv2rgb from './hsv2rgb.glsl.js';
export default `#version 300 es

${hsv2rgb}

in vec3 hsv;
out vec4 color;

void main() {
  color = vec4(hsv2Rgb(hsv), 1);
}
`;

然后在一些程序中使用

import someFragmentShaderSource from './somefragmentshader.glsl.js';
...
...compile shader using someFragmentShaderSource ...

如果您不喜欢使用模板字符串替换,那么制作自己的字符串是微不足道的

const subs = {
  hsv2rgb: `...code-from-above--...`,
  rgb2hsv: `...some code ...`,
};

// replace `#include <name>` with named sub
function replaceSubs(str, subs) {
  return str.replace(/#include\s+<(\w+)>/g, (m, key) => {
    return subs[key];
  });
}

接着

const fragmentShader2 = replaceSubs(`#version 300 es

#include <hsv2rgb>

in vec3 hsv;
out vec4 color;

void main() {
  color = vec4(hsv2Rgb(hsv), 1);
}
`, snippets);

推荐阅读