Glsl WebGL2:不同着色器中的相同函数

Glsl WebGL2:不同着色器中的相同函数,glsl,fragment-shader,webgl2,Glsl,Fragment Shader,Webgl2,我有一个包含复杂数学的大函数,我想在几个片段着色器中调用相同的函数。是否必须将函数的代码复制粘贴到每个着色器?或者有没有办法避免这种情况,在着色器之间共享代码?我可以为常用着色器函数提供任何类型的“库”吗?在OpenGL/WebGL中,GLSL代码作为文本传递。因此,如果您有一个可在多个GLSL程序中重用的函数,那么您可以编写一个着色器管理器来连接各种着色器块 有几种常见的方法: Mega Shader由所有GLSL程序共享,在代码中使用#ifdefs来激活/停用特定块。可能会变得非常混乱 着

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

OpenGL/WebGL中,GLSL代码作为文本传递。因此,如果您有一个可在多个GLSL程序中重用的函数,那么您可以编写一个着色器管理器来连接各种着色器块

有几种常见的方法:

  • Mega Shader由所有GLSL程序共享,在代码中使用
    #ifdefs
    来激活/停用特定块。可能会变得非常混乱
  • 着色器管理器根据字符串常量动态构造GLSL程序(如代码生成)
  • 着色器管理器使用预定义的标准函数列表替换子字符串。核心GLSL语法不支持
    #include
    指令,但着色器管理器可能会实现它们,或者使用另一种语法来标识要替换的子字符串,如
    %ColorLighting%
    (或者在JavaScript的情况下只使用
    ${theVariable}
因此JavaScript中的示例可能如下所示:

//可重用的GLSL函数
var getColor_Red=“vec4 getColor(){返回vec4(1.0,0.0,0.0,1.0);}\n”
//片段着色器生成器
函数getFragShaderRed(){
返回“精度高浮点值;\n”
+getColor_红色
+“void main(){gl_FragColor=getColor();}”;
}
下面是一个涵盖非WebGL案例的较长答案

桌面OpenGL在这种情况下提供了更大的灵活性-它允许将同一阶段的多个着色器连接到单个GLSL程序。这意味着,专用函数可以被移动到专用着色器中,在其他无需体的函数的前向声明中重用,并在多个GLSL程序中链接,类似地,C++程序通常是如何编译和链接的。
const GLchar*aShader1Text=
“vec4 getColor(){返回vec4(1.0,0.0,0.0,1.0);}”;
GLuint aShader1Id=glCreateShader(GL_片段_着色器);
glShaderSource(aShader1Id,1和aShader1Text,NULL);
glCompileShader(aShader1Id);
常量GLchar*aShader2Text=
“vec4 getColor();”//转发声明
“void main(){gl_FragColor=getColor();}”
GLuint aShader2Id=glCreateShader(GL_片段_着色器);
glShaderSource(aShader2Id,1和aShader2Text,NULL);
glCompileShader(aShader2Id);
GLuint aProgramID=glCreateProgram();
glAttachShader(程序ID,aShader0Id);//一些顶点着色器
glAttachShader(程序ID,aShader1Id);//片段着色器块1
glAttachShader(程序ID,aShader2Id);//片段着色器块2
glLinkProgram(一个程序ID);
此功能有两个问题:

    <> LI>与C++程序不同,<强> OpenGL驱动程序通常不真正编译“单独的着色器对象< /Stime>”,而是验证其语法,而真正的编译是在整个GLSL程序的“链接”阶段完成的。与字符串串联和重新编译整个GLSL程序源代码相比,这实际上消除了编译单个GLSL块的任何好处(例如,从性能角度)
  • OpenGL ESWebGL刚刚从其规范中删除了此功能,因此便携程序无法依赖桌面OpenGL中提供的此功能(从GLSL引入之初)。API本身是相同的,但是如果没有
    main()
    函数,OpenGL驱动程序将无法编译GLSL着色器

桌面OpenGL 4.0引入了另一项功能,它为GLSL程序定义提供了更大的灵活性,使其在运行时可配置。这是相当复杂的功能,对于静态GLSL程序来说不太合理,而且在OpenGL ES/WebGL中也不可用。

OpenGL/WebGL中,GLSL代码作为文本传递。因此,如果您有一个可在多个GLSL程序中重用的函数,那么您可以编写一个着色器管理器来连接各种着色器块

有几种常见的方法:

  • Mega Shader由所有GLSL程序共享,在代码中使用
    #ifdefs
    来激活/停用特定块。可能会变得非常混乱
  • 着色器管理器根据字符串常量动态构造GLSL程序(如代码生成)
  • 着色器管理器使用预定义的标准函数列表替换子字符串。核心GLSL语法不支持
    #include
    指令,但着色器管理器可能会实现它们,或者使用另一种语法来标识要替换的子字符串,如
    %ColorLighting%
    (或者在JavaScript的情况下只使用
    ${theVariable}
因此JavaScript中的示例可能如下所示:

//可重用的GLSL函数
var getColor_Red=“vec4 getColor(){返回vec4(1.0,0.0,0.0,1.0);}\n”
//片段着色器生成器
函数getFragShaderRed(){
返回“精度高浮点值;\n”
+getColor_红色
+“void main(){gl_FragColor=getColor();}”;
}
下面是一个涵盖非WebGL案例的较长答案

桌面OpenGL在这种情况下提供了更大的灵活性-它允许将同一阶段的多个着色器连接到单个GLSL程序。这意味着,专用函数可以被移动到专用着色器中,在其他无需体的函数的前向声明中重用,并在多个GLSL程序中链接,类似地,C++程序通常是如何编译和链接的。
const GLchar*aShader1Text=
vec4 getColor(){返回vec4(1.0,0.0,0.0,1.0)
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);
}
`;
/* 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);