提问人:asimes 提问时间:9/29/2014 更新时间:5/6/2021 访问量:10813
将 JavaScript 变量发送到片段着色器
Sending JavaScript variables to fragment shader
问:
我一直在拼凑在线示例来制作 Mandelbrot Set 片段着色器。顶点着色器基本上什么都不做,它分配,片段着色器做一些数学运算来计算图像。gl_Position
但是,我有许多 s 想用 JavaScript 控制的变量替换,但我不知道该怎么做。如果可以在下面的代码中显示一个关于如何用 JavaScript 分配的变量替换的例子,我可能会弄清楚其余的。我相信我需要指定一个或但不确定如何管理从 JavaScript 到 GLSL 的通信。#define
#define MAX_ITERATIONS 200
uniform
varying
另外,我不明白JavaScript和顶点着色器之间是如何工作的,我所拥有的与示例基本相同。aPosition
JavaScript,我想只对 SO 读者很重要,其余的如果需要,就会发布:init()
var canvas, gl, shaderProgram;
function draw() {
window.requestAnimationFrame(draw, canvas);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function init() {
canvas = document.getElementById("theCanvas");
gl = initGl(canvas);
if (!gl) {
alert("Could not initialize WebGL");
return;
}
shaderProgram = initShaders();
if (!shaderProgram) {
alert("Could not initialize shaders");
return;
}
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0,
]),
gl.STATIC_DRAW
);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
var aPosition = gl.getAttribLocation(shaderProgram, "aPosition");
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
draw();
}
function initGl(inCanvas) {
gl = false;
try { gl = inCanvas.getContext("webgl") || inCanvas.getContext("experimental-webgl"); }
catch (e) {}
return !gl ? false : gl;
}
function initShaders() {
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, document.getElementById("vertexShader").text);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(vertexShader));
return false;
}
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, document.getElementById("fragmentShader").text);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(fragmentShader));
return false;
}
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) return false;
gl.useProgram(shaderProgram);
return shaderProgram;
}
顶点着色器:
attribute vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
片段着色器、 、 、 和 应该在 JavaScript 中控制:MAX_ITERATIONS
XMIN
YMIN
WH
#ifdef GL_FRAGEMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
precision mediump int;
#define MAX_ITERATIONS 200
#define XMIN -2.5
#define YMIN -2.0
#define WH 4.0
#define LOG_TWO log(2.0)
#define LOG_MAX log(200.0)
void main() {
// Normalized pixel position to complex plane position
float maxPwh = max(640.0, 480.0);
float x = XMIN+(gl_FragCoord.x/maxPwh)*WH;
float y = YMIN+(gl_FragCoord.y/maxPwh)*WH;
// Complex plane window offsets for pixel windows that are not square
float halfDelta = WH/maxPwh*0.5;
x -= min((640.0-480.0)*halfDelta, 0.0);
y -= min((480.0-640.0)*halfDelta, 0.0);
// Mandelbrot Set code
float zr = x;
float zi = y;
int iterations = 0;
for (int i = 0; i < MAX_ITERATIONS; i++) {
iterations = i;
float sqZr = zr*zr;
float sqZi = zi*zi;
float twoZri = 2.0*zr*zi;
zr = sqZr-sqZi+x;
zi = twoZri+y;
if (sqZr+sqZi > 16.0) break;
}
if (iterations == MAX_ITERATIONS-1) gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
else {
float fn = float(iterations)+1.0-log(log(sqrt(zr*zr+zi*zi)))/LOG_TWO;
float logVal = log(fn)/LOG_MAX;
gl_FragColor = vec4(logVal, logVal, logVal, 1.0);
}
}
答:
简短的回答是,您基本上有 2 个选择
通过统一将值从 JavaScript 传递到 GLSL。
例如,如果要传递浮点数,请创建浮点数制服
uniform float foo;
在 JavaScript 中,编译并链接该着色器,然后查找制服的位置
var locationOfFoo = gl.getUniformLocation(someProgram, "foo");
现在,您可以使用以下命令将值传递给 GLSL:
gl.useProgram(someProgram) gl.uniform1f(locationOfFoo, valueToPass);
在编译着色器之前操作字符串
#define MAX_INTERATIONS %maxIterations% #define XMIN %xMin%
...
var maxIterations = 123; var xMin = 4.5; shaderSource = shaderSource.replace(/%maxIterations%/g, maxIterations); shaderSource = shaderSource.replace(/%xMin%/g, xMin);
(1)以上是用于传递经常变化的东西。#2 用于在编译之前更改着色器。#1 是一种几乎 100% 用于 WebGL 程序的技术。#2 在动态生成着色器时经常使用,许多游戏引擎都会这样做。
我花了大约 45 分钟来实现 gman 的答案,因为 我一直在犯愚蠢的小错误。所以这里有一个完整的工作代码 创建可调整切片贴图的示例。
测试语言:Chrome、Internet Explorer 和 Edge:
<!DOCTYPE HTML >
<html lang="en">
<head>
<meta charset="UTF-8">
<title> GL_TILE_TESTBED </title>
<!-- AUTHOR: John Mark Isaac Madison -->
<!-- EMAIL : [email protected] -->
<!-- SSSSSSSSS SHADER_SECTION START SSSSSSSSS -->
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<style>
p{ font-size:12pt;}
h3,p,input,button,br{
padding:0px;
margin:0px;
font-family:"Andale Mono";
}
button,input{
padding:10px;
}
</style>
<script id="VERT_SHADER" type="NOT_JAVASCRIPT">
precision highp float;
attribute vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
</script>
<script id="FRAG_SHADER" type="NOT_JAVASCRIPT">
//Must declare precision before declaring
//any uniforms:
////////////////////////////////////////////////
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
precision mediump int;
////////////////////////////////////////////////
#define CANVAS_WID 640.0
#define CANVAS_HIG 480.0
#define TIL_WID 64.0
#define TIL_HIG 64.0
//Uniforms exposed to HTML/JAVASCRIPT:
uniform float TIL_WID_EDIT;
uniform float TIL_HIG_EDIT;
float til_wid;
float til_hig;
void main() {
//If uniforms have not set by user,
//use the default values set by the #define(s)
//==========================================//
if(TIL_WID_EDIT > 0.0){
til_wid = TIL_WID_EDIT;
}else{
til_wid = TIL_WID;
}
if(TIL_HIG_EDIT > 0.0){
til_hig = TIL_HIG_EDIT;
}else{
til_hig = TIL_HIG;
}
//==========================================//
//NOTE: on "gl_FragCoord" range:
//******************************************//
//web-gl: In terms of pixel/canvas coords.
//OpenGL: In terms of 0 to 1.
//******************************************//
//:Calculate number of tiles shown on screen:
//:This may be fractional:
float NUM_TIL_X = CANVAS_WID / til_wid;
float NUM_TIL_Y = CANVAS_HIG / til_hig;
vec2 FC_MOD;
FC_MOD.x = gl_FragCoord.x;
FC_MOD.y = gl_FragCoord.y;
//You want all tiles to have the full range
//of colors, so you always modulate by
//CANVAS_WID and CANVAS_HIG, You scale by the
//# of tiles on each axis which means the
//gradient becomes steeper as the # of tiles
//increases.
FC_MOD.x = mod( gl_FragCoord.x*NUM_TIL_X, CANVAS_WID );
FC_MOD.y = mod( gl_FragCoord.y*NUM_TIL_Y, CANVAS_HIG );
//[N]ormalize values into range 0 to 1:
//NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN//
float norm_X = (FC_MOD.x) / CANVAS_WID;
float norm_Y = (FC_MOD.y) / CANVAS_HIG;
//NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN//
//Use [B]lue channel because why not?
//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB//
float GRAD_X = gl_FragCoord.x / CANVAS_WID;
//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB//
//Set the final [F]ragment colors:
//FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF//
gl_FragColor = vec4(norm_X, norm_Y, GRAD_X, 1.0);
//FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF//
}
</script>
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- SSSSSSSSSS SHADER_SECTION END SSSSSSSSSS -->
</head>
<!-- HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH -->
<!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->
<body onload="ON_LOADED_FUNCTION()" >
<!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->
<h3> Open GL Tile TestBed <h3>
<p> Author: John Mark Isaac Madison <p>
<p> Email : [email protected] <p>
<canvas id="glCanvas"></canvas>
</br>
<button onClick="PUT_WID();">TILE_WIDTH__IN_PIXELS</button>
<input type="text" id="INPUT_WID" value="45">
</br>
</br>
<button onClick="PUT_HIG();">TILE_HEIGHT_IN_PIXELS</button>
<input type="text" id="INPUT_HIG" value="45">
</br>
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<script id="BOILER_PLATE_CODE">
function ON_LOADED_FUNCTION(){
console.log("[ON_LOADED_FUNCTION]");
main();
}
//:Takes the gl context object, if the input
//:is null, we likely failed to get the
//:context.
function HAS_OPEN_GL_CHECK(gl){
// Only continue if WebGL is
// available and working
if (!gl) {
var msg = "";
msg += "[Unable to initialize WebGL.]";
msg += "[your browser or machine may]";
msg += "[not support it.]"
alert( msg );
return;
}
}
function GET_INPUT_BOX_VALUE( elem_id ){
var box; //DOM input box
var val; //Value in input box.
box = document.getElementById( elem_id );
val = box.value;
return (0 + val); //cast to number.
}
function PUT_WID(){
assert_program_and_gl_exist();
var val = GET_INPUT_BOX_VALUE("INPUT_WID");
SET_ATTR("TIL_WID_EDIT", val);
}
function PUT_HIG(){
assert_program_and_gl_exist();
var val = GET_INPUT_BOX_VALUE("INPUT_HIG");
SET_ATTR("TIL_HIG_EDIT", val);
}
function SET_ATTR(gl_var_name, val){
if(val < 0 || val > 256 ){
alert("choose value between 0 to 256");
return;
}
var loc; //<--location of variable.
loc = gl.getUniformLocation(
program ,
gl_var_name
);
gl.useProgram(program);
gl.uniform1f(loc, val);
}
function assert_program_and_gl_exist(){
if(!program){慌("[NO_PROGRAM_EXISTS]");}
if(!gl ){慌("[NO_GL_EXISTS]");}
}
//慌: "disconcerted, be confused, lose one's head"
//慌: In Code: ~Panic~
function 慌( panic_message ){
console.log( panic_message );
alert ( panic_message );
throw ( panic_message );
}
function makeOpenGlContextUsingCanvas(c){
//:Try what works in chrome and all the
//:respectable browsers first:
gl = c.getContext("webgl");
if(!gl){
console.log("[Probably_In_IE]");
gl = c.getContext("experimental-webgl");
}else{
console.log("[Probably_NOT_IE]");
}
HAS_OPEN_GL_CHECK( gl );
return gl;
}
//: No "var" prefix, making them global:
function initGlobals(){
canvas = document.querySelector("#glCanvas");
if(!canvas){
alert("FAILED_TO_GET_CANVAS");
}else{
console.log("[GOT_CANVAS]");
}
gl = makeOpenGlContextUsingCanvas(canvas);
//These dimensions are hard-coded into
//fragment shader code, so be careful
//about changing them:
canvas.width = 640;
canvas.height= 480;
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
-1.0, 1.0,
1.0, -1.0,
1.0, 1.0]),
gl.STATIC_DRAW
);
//G == Global Container.
//To fix problems with rendering in I.E.
//(Internet Explorer)
//GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG//
var G = {};
G.canvas = canvas;
G.gl = gl;
G.buffer = buffer;
if( ! G.canvas ||
! G.gl ||
! G.buffer ){
慌("[Global_Container_Broken]");
}
return G;
//GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG//
}
function main(){
G = initGlobals();
HAS_OPEN_GL_CHECK( G );
gl.viewport(0,0,gl.drawingBufferWidth, gl.drawingBufferHeight);
setup();
render();
}
function setup(){
var frag_dom = document.getElementById("FRAG_SHADER");
var frag_src = frag_dom.text;
console.log( frag_src );
F = createShader(
gl,gl.FRAGMENT_SHADER, frag_src
);
var vert_dom = document.getElementById("VERT_SHADER");
var vert_src = vert_dom.text;
console.log( vert_src );
V = createShader(
gl, gl.VERTEX_SHADER, vert_src
);
//**** MAKE "program" a GLOBAL VAR ****//
program = createProgram(gl,V,F);
gl.useProgram( program );
if(!program){
慌("PROGRAM_IS_NULL");
}
}
function render(){
window.requestAnimationFrame(render,canvas);
// Set clear color to black, fully opaque
gl.clearColor(0.0, 0.0, 0.5, 1.0);
// Clear the color buffer with specified clear color
gl.clear(gl.COLOR_BUFFER_BIT);
//Directly before call to gl.drawArrays:
positionLocation = gl.getAttribLocation(program, "a_position");
gl.enableVertexAttribArray( positionLocation );
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
function createShader(gl,type,source){
//:Error Check For Bad Inputs:
if(!gl ){慌("[NULL_GL]");}
if(!type ){慌("[NULL_TY]");}
if(!source){慌("[NULL_SR]");}
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
var res = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if( res ){
console.log("[SHADER_COMPILED!]");
return shader;
}
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
慌("[FAILED_TO_COMPILE_SHADER]");
}
//:gl : openGL context :
//:vert: vertex shader :
//:frag: fragment shader:
function createProgram(gl,vert, frag){
var program = gl.createProgram();
gl.attachShader(program, vert);
gl.attachShader(program, frag);
gl.linkProgram (program);
var res = gl.getProgramParameter(program, gl.LINK_STATUS);
if( res ){
console.log("[PROGRAM_CREATED!]");
return program;
}
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
</script>
<!-- SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS -->
<!-- BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -->
</body>
</html>
评论