|
@@ -0,0 +1,836 @@
|
|
|
+<!DOCTYPE HTML>
|
|
|
+<html lang="en">
|
|
|
+
|
|
|
+<head>
|
|
|
+ <title>Shader Sandbox</title>
|
|
|
+ <meta charset="utf-8"></meta>
|
|
|
+</head>
|
|
|
+
|
|
|
+<body style="background: #191919">
|
|
|
+
|
|
|
+
|
|
|
+<link href="css/sandbox.css" rel="stylesheet" type="text/css"/>
|
|
|
+<link href="css/super_slider.css" media="screen" rel="stylesheet" type="text/css"/>
|
|
|
+<link href="css/super_picker.css" media="screen" rel="stylesheet" type="text/css"/>
|
|
|
+<link href="css/plugin.css" media="screen" rel="stylesheet" type="text/css"/>
|
|
|
+<link href="css/ace_theme_tweaks.css" rel="stylesheet" type="text/css"/>
|
|
|
+<link href="css/jquery.ui.resizable.css" rel="stylesheet">
|
|
|
+
|
|
|
+
|
|
|
+<div id="peekaboo" style="display: none; float: left; margin: 0px 8px 0px 0px; width: 750px; height: 600px; position: relative">
|
|
|
+ <pre id="editor"></pre>
|
|
|
+</div>
|
|
|
+
|
|
|
+
|
|
|
+<div id="stuff" style="display: none; overflow: auto; height: 100%">
|
|
|
+
|
|
|
+<div style="float: left; width: 512px; margin: 8px">
|
|
|
+ <div id="effect" style="width: 512px; height: 512px; float: left; position: relative;"></div>
|
|
|
+ <div id="status" style="min-width: 496px">
|
|
|
+ <p id="status_text"></p>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+
|
|
|
+<div id="params"></div>
|
|
|
+
|
|
|
+</div>
|
|
|
+
|
|
|
+
|
|
|
+<script>
|
|
|
+ function Color(r, g, b) {
|
|
|
+ this.r = r;
|
|
|
+ this.g = g;
|
|
|
+ this.b = b;
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+
|
|
|
+<script src="js/color_picker.js" type="text/javascript"></script>
|
|
|
+<script src="js/jquery-1.6.1.min.js"></script>
|
|
|
+<script src="js/ace/ace.js" type="text/javascript" charset="utf-8"></script>
|
|
|
+<script src="js/ace/theme-clouds_midnight.js" type="text/javascript" charset="utf-8"></script>
|
|
|
+<script src="js/ace/mode-glsl.js" type="text/javascript" charset="utf-8"></script>
|
|
|
+<script src="js/super_picker.js" type="text/javascript"></script>
|
|
|
+<script src="js/super_slider.js" type="text/javascript"></script>
|
|
|
+<script src="js/jquery.mousewheel.min.js" type="text/javascript"></script>
|
|
|
+
|
|
|
+<script src="js/macton/macton-utils.js" type="text/javascript"></script>
|
|
|
+<script src="js/macton/macton-gl-utils.js" type="text/javascript"></script>
|
|
|
+<script src="js/macton/webgl-utils.js" type="text/javascript"></script>
|
|
|
+<script src="js/macton/matrix4x4.js" type="text/javascript"></script>
|
|
|
+<script src="js/macton/cameracontroller.js" type="text/javascript"></script>
|
|
|
+
|
|
|
+<script src="js/jquery-ui-1.8.13.custom.min.js" type="text/javascript"></script>
|
|
|
+
|
|
|
+<script src="js/ltc_tables.js" type="text/javascript"></script>
|
|
|
+
|
|
|
+
|
|
|
+<script>
|
|
|
+var g_vshader = null;
|
|
|
+
|
|
|
+var g_param_types = {};
|
|
|
+var g_params = {};
|
|
|
+var g_param_edited = {};
|
|
|
+var g_param_default = {};
|
|
|
+
|
|
|
+var g_sample_count = 0;
|
|
|
+
|
|
|
+var bindSlider = function(name, label, default_value, value, state) {
|
|
|
+ var slider = new SuperSlider(name, {
|
|
|
+ label: label,
|
|
|
+ default_value: default_value,
|
|
|
+ value: value,
|
|
|
+ min: state.min,
|
|
|
+ max: state.max,
|
|
|
+ step: state.step });
|
|
|
+
|
|
|
+ slider.bind("change", function(event) {
|
|
|
+ g_params[name] = event.target.val;
|
|
|
+ g_param_edited[name] = true;
|
|
|
+ g_sample_count = 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ slider.bind("reset", function(event) {
|
|
|
+ g_param_edited[name] = undefined;
|
|
|
+ g_sample_count = 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ return slider;
|
|
|
+}
|
|
|
+
|
|
|
+var bindPicker = function(name, label, default_value, value) {
|
|
|
+ var picker = new SuperPicker(name, {
|
|
|
+ label: label,
|
|
|
+ default_value: default_value,
|
|
|
+ value: value,
|
|
|
+ callback: function(col) {
|
|
|
+ g_params[name] = col;
|
|
|
+ g_param_edited[name] = true;
|
|
|
+ g_sample_count = 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ picker.bind("reset", function (event) {
|
|
|
+ g_param_edited[name] = undefined;
|
|
|
+ g_sample_count = 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ return picker;
|
|
|
+}
|
|
|
+
|
|
|
+var bindCheckbox = function(name, label, value) {
|
|
|
+ var control = $("<input>")
|
|
|
+ .attr({ type: "checkbox", /*id: name,*/ checked: value })
|
|
|
+ .css("margin", "0 0 0 4px");
|
|
|
+
|
|
|
+ var cb_label = $("<label>")
|
|
|
+ .attr("for", name)
|
|
|
+ .text(label)
|
|
|
+ .addClass("checkbox_label");
|
|
|
+
|
|
|
+ cb_label.append(control);
|
|
|
+
|
|
|
+ control.bind("change", function (event) {
|
|
|
+ g_params[name] = event.target.checked;
|
|
|
+ g_param_edited[name] = true;
|
|
|
+ g_sample_count = 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ cb_label.bind("click", function (event) {
|
|
|
+ control.prop("checked", g_param_default[name]);
|
|
|
+ control.trigger("change");
|
|
|
+ g_param_edited[name] = undefined;
|
|
|
+ g_sample_count = 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ control.bind("click", function (event) {
|
|
|
+ event.stopPropagation();
|
|
|
+ });
|
|
|
+
|
|
|
+ return cb_label;
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+
|
|
|
+<script type="text/javascript">
|
|
|
+/**
|
|
|
+ * Provides requestAnimationFrame in a cross browser way.
|
|
|
+ * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
|
|
+ */
|
|
|
+if (!window.requestAnimationFrame) {
|
|
|
+ window.requestAnimationFrame = (function() {
|
|
|
+ return window.webkitRequestAnimationFrame ||
|
|
|
+ window.mozRequestAnimationFrame ||
|
|
|
+ window.oRequestAnimationFrame ||
|
|
|
+ window.msRequestAnimationFrame ||
|
|
|
+ function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
|
|
|
+ window.setTimeout(callback, 1000/60);
|
|
|
+ };
|
|
|
+ })();
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+
|
|
|
+<script type="text/javascript">
|
|
|
+var effectdiv,
|
|
|
+ sourcediv,
|
|
|
+ canvas,
|
|
|
+ gl,
|
|
|
+ buffer,
|
|
|
+ vertex_shader,
|
|
|
+ fragment_shader,
|
|
|
+ currentprogram,
|
|
|
+ vertexpositionlocation,
|
|
|
+ texturelocation,
|
|
|
+ parameters = {
|
|
|
+ start_time: new date().gettime(),
|
|
|
+ time: 0,
|
|
|
+ screenwidth: 0,
|
|
|
+ screenheight: 0
|
|
|
+ };
|
|
|
+
|
|
|
+var g_zoom = 0;
|
|
|
+
|
|
|
+var model = new matrix4x4();
|
|
|
+var view = new matrix4x4();
|
|
|
+var projection = new matrix4x4();
|
|
|
+var controller = null;
|
|
|
+
|
|
|
+var ltc_mat_texture = null;
|
|
|
+var ltc_mag_texture = null;
|
|
|
+
|
|
|
+var rttframebuffer = null;
|
|
|
+var rtttexture = null;
|
|
|
+
|
|
|
+var blit_vs = null;
|
|
|
+var blit_fs = null;
|
|
|
+var blitprogram = null;
|
|
|
+
|
|
|
+
|
|
|
+function fetchfile(url, cache)
|
|
|
+{
|
|
|
+ var text = $.ajax({
|
|
|
+ url: url,
|
|
|
+ async: false,
|
|
|
+ datatype: "text",
|
|
|
+ mimetype: "text/plain",
|
|
|
+ cache: cache,
|
|
|
+ }).responsetext;
|
|
|
+
|
|
|
+ return text;
|
|
|
+}
|
|
|
+
|
|
|
+function setclampedtexturestate()
|
|
|
+{
|
|
|
+ gl.texparameteri(gl.texture_2d, gl.texture_min_filter, gl.nearest);
|
|
|
+ gl.texparameteri(gl.texture_2d, gl.texture_mag_filter, gl.linear);
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+function init() {
|
|
|
+ vertex_shader = fetchfile("shaders/ltc/ltc.vs", false);
|
|
|
+ fragment_shader = fetchfile("shaders/ltc/ltc.fs", false);
|
|
|
+
|
|
|
+ g_vshader = vertex_shader;
|
|
|
+ $("#editor").text(fragment_shader);
|
|
|
+
|
|
|
+ canvas = document.createelement("canvas");
|
|
|
+ canvas.style.csstext = "border: 2px solid #404040; border-radius: 6px";
|
|
|
+
|
|
|
+ effectdiv = document.getelementbyid("effect");
|
|
|
+ effectdiv.appendchild(canvas);
|
|
|
+
|
|
|
+ // initialise webgl
|
|
|
+ try {
|
|
|
+ gl = canvas.getcontext("experimental-webgl");
|
|
|
+ } catch(error) { }
|
|
|
+
|
|
|
+ if (!gl) {
|
|
|
+ alert("webgl not supported");
|
|
|
+ throw "cannot create webgl context";
|
|
|
+ }
|
|
|
+
|
|
|
+ // check for float-rt support
|
|
|
+ if (!gl.getextension("oes_texture_float")) {
|
|
|
+ alert("oes_texture_float not supported");
|
|
|
+ throw "missing webgl extension";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!gl.getextension("oes_texture_float_linear")) {
|
|
|
+ alert("oes_texture_float_linear not supported");
|
|
|
+ throw "missing webgl extension";
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // add enum strings to context
|
|
|
+ if (gl.enum_strings === undefined) {
|
|
|
+ gl.enum_strings = { };
|
|
|
+ for (var propertyname in gl) {
|
|
|
+ if (typeof gl[propertyname] == "number") {
|
|
|
+ gl.enum_strings[gl[propertyname]] = propertyname;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // create vertex buffer (2 triangles)
|
|
|
+ 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);
|
|
|
+
|
|
|
+ // create program
|
|
|
+ currentprogram = createprogram(vertex_shader, fragment_shader);
|
|
|
+
|
|
|
+ ////
|
|
|
+ rttframebuffer = gl.createframebuffer();
|
|
|
+ gl.bindframebuffer(gl.framebuffer, rttframebuffer);
|
|
|
+ rttframebuffer.width = 512; // fixme
|
|
|
+ rttframebuffer.height = 512;
|
|
|
+
|
|
|
+ rtttexture = gl.createtexture();
|
|
|
+ gl.bindtexture(gl.texture_2d, rtttexture);
|
|
|
+ gl.teximage2d(gl.texture_2d, 0, gl.rgba,
|
|
|
+ rttframebuffer.width, rttframebuffer.height,
|
|
|
+ 0, gl.rgba, gl.float, null);
|
|
|
+
|
|
|
+ gl.framebuffertexture2d(gl.framebuffer, gl.color_attachment0, gl.texture_2d, rtttexture, 0);
|
|
|
+ gl.bindframebuffer(gl.framebuffer, null);
|
|
|
+
|
|
|
+ gl.bindtexture(gl.texture_2d, null);
|
|
|
+
|
|
|
+
|
|
|
+ blit_vs = fetchfile("shaders/ltc/ltc_blit.vs", false);
|
|
|
+ blit_fs = fetchfile("shaders/ltc/ltc_blit.fs", false);
|
|
|
+
|
|
|
+ var header = "#ifdef gl_es\nprecision highp float;\n#endif\n#line 0\n";
|
|
|
+ blit_fs = header + blit_fs;
|
|
|
+
|
|
|
+ blitprogram = gl.createprogram();
|
|
|
+
|
|
|
+ var vs = createshader(blit_vs, gl.vertex_shader);
|
|
|
+ var fs = createshader(blit_fs, gl.fragment_shader);
|
|
|
+
|
|
|
+ gl.attachshader(blitprogram, vs);
|
|
|
+ gl.attachshader(blitprogram, fs);
|
|
|
+
|
|
|
+ gl.deleteshader(vs);
|
|
|
+ gl.deleteshader(fs);
|
|
|
+
|
|
|
+ gl.linkprogram(blitprogram);
|
|
|
+ ////
|
|
|
+
|
|
|
+ onwindowresize();
|
|
|
+ window.addeventlistener("resize", onwindowresize, false);
|
|
|
+
|
|
|
+ $("canvas").mousewheel(function(event, delta) {
|
|
|
+ g_zoom += delta*10.0;
|
|
|
+ g_sample_count = 0;
|
|
|
+ // console.log("pagex: " + event.pagex + " pagey: " + event.pagey);
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+
|
|
|
+ $("canvas").attr("tabindex", "0").keydown(function(event) {
|
|
|
+ //console.log(event);
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+
|
|
|
+ // Load fitted brdf data as float texture
|
|
|
+ // 64 x 64 texels of 4 floats each
|
|
|
+ ltc_mat_texture = gl.createtexture();
|
|
|
+ gl.bindtexture(gl.texture_2d, ltc_mat_texture);
|
|
|
+ gl.teximage2d(gl.texture_2d, 0, gl.rgba, 64, 64, 0, gl.rgba, gl.float, new float32array(g_ltc_mat));
|
|
|
+ setclampedtexturestate();
|
|
|
+
|
|
|
+ // 64 x 64 texels of 1 float each
|
|
|
+ ltc_mag_texture = gl.createtexture();
|
|
|
+ gl.bindtexture(gl.texture_2d, ltc_mag_texture);
|
|
|
+ gl.teximage2d(gl.texture_2d, 0, gl.alpha, 64, 64, 0, gl.alpha, gl.float, new float32array(g_ltc_mag));
|
|
|
+ setclampedtexturestate();
|
|
|
+
|
|
|
+ // fetch media and kick off the main program once everything has loaded
|
|
|
+ $(function() {
|
|
|
+ $(document).ajaxstop(function() {
|
|
|
+ $(this).unbind("ajaxstop");
|
|
|
+ main_prog();
|
|
|
+ });
|
|
|
+
|
|
|
+ // load data here
|
|
|
+
|
|
|
+ main_prog();
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function onwindowresize(event) {
|
|
|
+ $("#peekaboo").css("height", window.innerheight);
|
|
|
+ $("#stuff").css("height", window.innerheight);
|
|
|
+
|
|
|
+ canvas.width = 512; // window.innerwidth;
|
|
|
+ canvas.height = 512; // window.innerheight;
|
|
|
+
|
|
|
+ parameters.screenwidth = canvas.width;
|
|
|
+ parameters.screenheight = canvas.height;
|
|
|
+
|
|
|
+ gl.viewport(0, 0, canvas.width, canvas.height);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function gluniformtypetostring(type) {
|
|
|
+ switch (type) {
|
|
|
+ case gl.float: return "float";
|
|
|
+ case gl.float_vec2: return "float_vec2";
|
|
|
+ case gl.float_vec3: return "float_vec3";
|
|
|
+ case gl.float_vec4: return "float_vec4";
|
|
|
+ case gl.int: return "int";
|
|
|
+ case gl.int_vec2: return "int_vec2";
|
|
|
+ case gl.int_vec3: return "int_vec3";
|
|
|
+ case gl.int_vec4: return "int_vec4";
|
|
|
+ case gl.bool: return "bool";
|
|
|
+ case gl.bool_vec2: return "bool_vec2";
|
|
|
+ case gl.bool_vec3: return "bool_vec3";
|
|
|
+ case gl.bool_vec4: return "bool_vec4";
|
|
|
+ case gl.float_mat2: return "float_mat2";
|
|
|
+ case gl.float_mat3: return "float_mat3";
|
|
|
+ case gl.float_mat4: return "float_mat4";
|
|
|
+ case gl.sampler_2d: return "sampler_2d";
|
|
|
+ case gl.sampler_cube: return "sampler_cube";
|
|
|
+ }
|
|
|
+
|
|
|
+ return "unknown";
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function createprogram(vertex, fragment) {
|
|
|
+ var header = "#ifdef gl_es\nprecision highp float;\n#endif\n#line 0\n";
|
|
|
+ fragment = header + fragment;
|
|
|
+
|
|
|
+ var program = gl.createprogram();
|
|
|
+
|
|
|
+ var vs = createshader(vertex, gl.vertex_shader);
|
|
|
+ var fs = createshader(fragment, gl.fragment_shader);
|
|
|
+
|
|
|
+ if (vs == null || fs == null)
|
|
|
+ return null;
|
|
|
+
|
|
|
+ gl.attachshader(program, vs);
|
|
|
+ gl.attachshader(program, fs);
|
|
|
+
|
|
|
+ gl.deleteshader(vs);
|
|
|
+ gl.deleteshader(fs);
|
|
|
+
|
|
|
+ gl.linkprogram(program);
|
|
|
+
|
|
|
+
|
|
|
+ if (!gl.getprogramparameter(program, gl.link_status)) {
|
|
|
+ /*
|
|
|
+ alert( "error:\n" +
|
|
|
+ "validate_status: " + gl.getprogramparameter( program, gl.validate_status ) + "\n" +
|
|
|
+ "error: " + gl.geterror() + "\n\n" +
|
|
|
+ "- vertex shader -\n" + vertex + "\n\n" +
|
|
|
+ "- fragment shader -\n" + fragment );
|
|
|
+ */
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ var control_names = new array();
|
|
|
+ var controls = {};
|
|
|
+
|
|
|
+ var lines = fragment.split("\n");
|
|
|
+
|
|
|
+ var bindings = { }
|
|
|
+
|
|
|
+ // adds double quotes around labels
|
|
|
+ function preprocessjson(str) {
|
|
|
+ return str.replace(/("(\\.|[^"])*"|'(\\.|[^'])*')|(\w+)\s*:/g,
|
|
|
+ function(all, string, strdouble, strsingle, jsonlabel) {
|
|
|
+ if (jsonlabel) {
|
|
|
+ return '"' + jsonlabel + '": ';
|
|
|
+ }
|
|
|
+ return all;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var x in lines) {
|
|
|
+ var line = lines[x];
|
|
|
+ var re = /^\/\/ bind\s+(.*?)\s(.*)/;
|
|
|
+ var m = re.exec(line);
|
|
|
+ if (m) {
|
|
|
+ try {
|
|
|
+ var obj = $.parsejson(preprocessjson(m[2]));
|
|
|
+ bindings[m[1]] = obj;
|
|
|
+ control_names.push(m[1]);
|
|
|
+ }
|
|
|
+ catch (e) {
|
|
|
+ console.log("failed to parse shader binding: " + m[1]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function copyproperties(src, dest) {
|
|
|
+ for (x in src) {
|
|
|
+ if (dest.hasownproperty(x) && typeof(dest[x]) === typeof(src[x]))
|
|
|
+ dest[x] = src[x];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // destroy existing children
|
|
|
+ var container = $("#params");
|
|
|
+
|
|
|
+ var children = container.children();
|
|
|
+ children.each(function() {
|
|
|
+ var v = $(this);
|
|
|
+ v.detach();
|
|
|
+ })
|
|
|
+ container.empty();
|
|
|
+
|
|
|
+ g_param_types = {};
|
|
|
+
|
|
|
+ var nb_uniforms = gl.getprogramparameter(program, gl.active_uniforms);
|
|
|
+ for (var i = 0; i < nb_uniforms; i++) {
|
|
|
+ var uni = gl.getactiveuniform(program, i);
|
|
|
+
|
|
|
+ var label = uni.name;
|
|
|
+ var bind = bindings[uni.name];
|
|
|
+ if (bind && typeof(bind.label) === "string")
|
|
|
+ label = bind.label;
|
|
|
+
|
|
|
+ var old_value = g_params[uni.name];
|
|
|
+ var is_default = !g_param_edited[uni.name];
|
|
|
+ var new_value = undefined;
|
|
|
+
|
|
|
+ var control = undefined;
|
|
|
+
|
|
|
+ if (uni.type === gl.float) {
|
|
|
+ var state = {
|
|
|
+ default : 1.0,
|
|
|
+ min : 0.0,
|
|
|
+ max : 1.0,
|
|
|
+ step : 0.01,
|
|
|
+ };
|
|
|
+
|
|
|
+ copyproperties(bind, state);
|
|
|
+
|
|
|
+ var is_default = !g_param_edited[uni.name];
|
|
|
+
|
|
|
+ new_value = is_default ? state.default : old_value;
|
|
|
+ control = bindslider(uni.name, label, state.default, new_value, state);
|
|
|
+ }
|
|
|
+ else if (uni.type === gl.float_vec3) {
|
|
|
+ var def_col = new color(1, 1, 1)
|
|
|
+
|
|
|
+ copyproperties(bind, def_col);
|
|
|
+
|
|
|
+ new_value = is_default ? def_col : old_value;
|
|
|
+ control = bindpicker(uni.name, label, def_col, new_value);
|
|
|
+ }
|
|
|
+ else if (uni.type === gl.bool) {
|
|
|
+ var def_val = true;
|
|
|
+
|
|
|
+ if (bind && typeof(bind.default) === "boolean")
|
|
|
+ def_val = bind.default;
|
|
|
+
|
|
|
+ g_param_default[uni.name] = def_val;
|
|
|
+
|
|
|
+ new_value = is_default ? def_val : old_value;
|
|
|
+ control = bindcheckbox(uni.name, label, new_value);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ g_param_types[uni.name] = uni.type;
|
|
|
+ g_params[uni.name] = new_value;
|
|
|
+
|
|
|
+ if (bind === undefined)
|
|
|
+ control_names.push(uni.name);
|
|
|
+ controls[uni.name] = control;
|
|
|
+ }
|
|
|
+
|
|
|
+ // finally add the controls to the page
|
|
|
+ for (x in control_names) {
|
|
|
+ var control = controls[control_names[x]];
|
|
|
+
|
|
|
+ // skip any binds that don't reference active uniforms
|
|
|
+ if (control === undefined)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // pad for next element
|
|
|
+ control.css("margin", "0 0 6px 0");
|
|
|
+
|
|
|
+ container.append(control);
|
|
|
+ }
|
|
|
+
|
|
|
+ return program;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+var gl_shader_error = null;
|
|
|
+
|
|
|
+function createshader(src, type) {
|
|
|
+ var shader = gl.createshader(type);
|
|
|
+
|
|
|
+ gl.shadersource(shader, src);
|
|
|
+ gl.compileshader(shader);
|
|
|
+
|
|
|
+ gl_shader_error = null;
|
|
|
+
|
|
|
+ if (!gl.getshaderparameter(shader, gl.compile_status)) {
|
|
|
+ gl_shader_error = gl.getshaderinfolog(shader);
|
|
|
+ // console.log((type == gl.vertex_shader ? "vertex" : "fragment") + " shader:\n" + gl.getshaderinfolog(shader));
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return shader;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function animate() {
|
|
|
+ requestanimationframe(animate);
|
|
|
+ draw();
|
|
|
+}
|
|
|
+
|
|
|
+function main_prog() {
|
|
|
+ controller = new cameracontroller(canvas);
|
|
|
+ // try the following (and uncomment the "pointer-events: none;" in
|
|
|
+ // the index.html) to try the more precise hit detection
|
|
|
+ // controller = new cameracontroller(document.getelementbyid("body"), c, gl);
|
|
|
+ controller.onchange = function(xrot, yrot) {
|
|
|
+ draw();
|
|
|
+ g_sample_count = 0;
|
|
|
+ };
|
|
|
+
|
|
|
+ requestanimationframe(animate);
|
|
|
+
|
|
|
+ draw();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function checkglerror() {
|
|
|
+ var error = gl.geterror();
|
|
|
+ if (error != gl.no_error) {
|
|
|
+ var str = "gl error: " + error + " " + gl.enum_strings[error];
|
|
|
+ console.log(str);
|
|
|
+ throw str;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function draw() {
|
|
|
+ parameters.time = new date().gettime() - parameters.start_time;
|
|
|
+
|
|
|
+ gl.enable(gl.depth_test);
|
|
|
+ gl.clearcolor(0.0, 0.0, 0.0, 0.0);
|
|
|
+
|
|
|
+ // note: the viewport is automatically set up to cover the entire canvas.
|
|
|
+ gl.clear(gl.color_buffer_bit | gl.depth_buffer_bit);
|
|
|
+
|
|
|
+ checkglerror();
|
|
|
+
|
|
|
+ // load program into gpu
|
|
|
+ gl.useprogram(currentprogram);
|
|
|
+
|
|
|
+ checkglerror();
|
|
|
+
|
|
|
+ // add in camera controller's rotation
|
|
|
+ view.loadidentity();
|
|
|
+ view.translate(0, 6, 0.1*g_zoom - 0.5);
|
|
|
+ view.rotate(controller.xrot - 10.0, 1, 0, 0);
|
|
|
+ view.rotate(controller.yrot, 0, 1, 0);;
|
|
|
+
|
|
|
+ // get var locations
|
|
|
+ vertexpositionlocation = gl.getattriblocation(currentprogram, "position");
|
|
|
+
|
|
|
+ function location(u) {
|
|
|
+ return gl.getuniformlocation(currentprogram, u);
|
|
|
+ }
|
|
|
+
|
|
|
+ // set values to program variables
|
|
|
+ for (var x in g_params) {
|
|
|
+ var type = g_param_types[x];
|
|
|
+ var value = g_params[x];
|
|
|
+ var loc = location(x);
|
|
|
+
|
|
|
+ if (type === gl.float) gl.uniform1f(loc, value);
|
|
|
+ if (type === gl.float_vec3) gl.uniform3f(loc, value.r, value.g, value.b);
|
|
|
+ if (type === gl.bool) gl.uniform1i(loc, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ gl.uniformmatrix4fv(location("view"), gl.false, new float32array(view.elements));
|
|
|
+ gl.uniform1f(location("time"), parameters.time/1000);
|
|
|
+ gl.uniform2f(location("resolution"), parameters.screenwidth, parameters.screenheight);
|
|
|
+
|
|
|
+ gl.activetexture(gl.texture0);
|
|
|
+ gl.bindtexture(gl.texture_2d, ltc_mat_texture);
|
|
|
+ gl.uniform1i(gl.getuniformlocation(currentprogram, "ltc_mat"), 0);
|
|
|
+
|
|
|
+ gl.activetexture(gl.texture1);
|
|
|
+ gl.bindtexture(gl.texture_2d, ltc_mag_texture);
|
|
|
+ gl.uniform1i(gl.getuniformlocation(currentprogram, "ltc_mag"), 1);
|
|
|
+
|
|
|
+ checkglerror();
|
|
|
+
|
|
|
+ gl.bindframebuffer(gl.framebuffer, rttframebuffer);
|
|
|
+
|
|
|
+ if (g_sample_count === 0) {
|
|
|
+ gl.clearcolor(0, 0, 0, 0);
|
|
|
+ gl.clear(gl.color_buffer_bit);
|
|
|
+ }
|
|
|
+
|
|
|
+ gl.uniform1i(location("samplecount"), g_sample_count);
|
|
|
+ g_sample_count += 8;
|
|
|
+
|
|
|
+ gl.enable(gl.blend);
|
|
|
+ gl.blendfunc(gl.one, gl.one);
|
|
|
+
|
|
|
+ // render geometry
|
|
|
+ gl.bindbuffer(gl.array_buffer, buffer);
|
|
|
+ gl.vertexattribpointer(vertexpositionlocation, 2, gl.float, false, 0, 0);
|
|
|
+ gl.enablevertexattribarray(vertexpositionlocation);
|
|
|
+ gl.drawarrays(gl.triangles, 0, 6);
|
|
|
+ gl.disablevertexattribarray(vertexpositionlocation);
|
|
|
+
|
|
|
+ gl.bindframebuffer(gl.framebuffer, null);
|
|
|
+ gl.useprogram(blitprogram);
|
|
|
+
|
|
|
+ gl.disable(gl.blend);
|
|
|
+
|
|
|
+ // set textures
|
|
|
+ gl.activetexture(gl.texture0);
|
|
|
+ gl.bindtexture(gl.texture_2d, rtttexture);
|
|
|
+ gl.uniform1i(gl.getuniformlocation(blitprogram, "tex"), 0);
|
|
|
+
|
|
|
+ gl.texparameteri(gl.texture_2d, gl.texture_min_filter, gl.nearest);
|
|
|
+ gl.texparameteri(gl.texture_2d, gl.texture_mag_filter, gl.nearest);
|
|
|
+ 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.uniform2f(gl.getuniformlocation(blitprogram, "resolution"), parameters.screenwidth, parameters.screenheight);
|
|
|
+
|
|
|
+ // blit pass
|
|
|
+ gl.enablevertexattribarray(vertexpositionlocation);
|
|
|
+ gl.drawarrays(gl.triangles, 0, 6);
|
|
|
+ gl.disablevertexattribarray(vertexpositionlocation);
|
|
|
+
|
|
|
+ gl.bindtexture(gl.texture_2d, null);
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+
|
|
|
+<script>
|
|
|
+ window.onload = function() {
|
|
|
+ init();
|
|
|
+
|
|
|
+ $("#peekaboo").resizable({ minWidth: 500 });
|
|
|
+ $("#peekaboo").show();
|
|
|
+
|
|
|
+ var editor = ace.edit("editor");
|
|
|
+ editor.setTheme("ace/theme/clouds_midnight");
|
|
|
+
|
|
|
+ var Mode = require("ace/mode/glsl").Mode;
|
|
|
+ editor.getSession().setMode(new Mode());
|
|
|
+ editor.getSession().setUseWrapMode(true);
|
|
|
+ editor.getSession().setOption("firstLineNumber", 0);
|
|
|
+ editor.setShowPrintMargin(false);
|
|
|
+
|
|
|
+ editor.renderer.setHScrollBarAlwaysVisible(false);
|
|
|
+ editor.renderer.setVScrollBarAlwaysVisible(false);
|
|
|
+
|
|
|
+ var markers = [];
|
|
|
+
|
|
|
+ var nb_sliders = 0;
|
|
|
+
|
|
|
+ var timeout = null;
|
|
|
+
|
|
|
+ $("#peekaboo").bind("resize", function(event, ui) {
|
|
|
+ editor.resize();
|
|
|
+ });
|
|
|
+
|
|
|
+ $("#peekaboo").css("height", window.innerHeight);
|
|
|
+
|
|
|
+ $("#stuff").show();
|
|
|
+
|
|
|
+ var rebuild = function() {
|
|
|
+ var vertex_shader, fragment_shader;
|
|
|
+ var new_prog;
|
|
|
+ var text = editor.getSession().getValue();
|
|
|
+
|
|
|
+ vertex_shader = g_vshader;
|
|
|
+ fragment_shader = text;
|
|
|
+
|
|
|
+ editor.getSession().clearAnnotations();
|
|
|
+ for (m in markers) {
|
|
|
+ editor.getSession().removeMarker(markers[m]);
|
|
|
+ }
|
|
|
+ markers = [];
|
|
|
+
|
|
|
+ new_prog = createProgram(vertex_shader, fragment_shader);
|
|
|
+
|
|
|
+ var status_color = new_prog ? "#404040" : "#7f0000"
|
|
|
+ $("#status").css("border-color", status_color);
|
|
|
+
|
|
|
+ marked_lines = [];
|
|
|
+
|
|
|
+ var text = "Compiled successfully!";
|
|
|
+
|
|
|
+ if (new_prog !== null) {
|
|
|
+ currentProgram = new_prog;
|
|
|
+ }
|
|
|
+ else if (gl_shader_error !== null) {
|
|
|
+ gl_shader_error = gl_shader_error.replace(/\0/g, "");
|
|
|
+ text = gl_shader_error.replace(/\n/g, "<br>");
|
|
|
+
|
|
|
+ var annos = [];
|
|
|
+
|
|
|
+ var re = /\d+:(\d+):(.*)\n/g;
|
|
|
+ var match;
|
|
|
+ while (match = re.exec(gl_shader_error)) {
|
|
|
+ var line = parseInt(match[1]);
|
|
|
+ var error = match[2];
|
|
|
+
|
|
|
+ if (marked_lines[line]) {
|
|
|
+ error = " **** " + error;
|
|
|
+ }
|
|
|
+
|
|
|
+ annos.push({
|
|
|
+ row: line,
|
|
|
+ column: 0,
|
|
|
+ text: error,
|
|
|
+ type: "error"
|
|
|
+ });
|
|
|
+
|
|
|
+ if (marked_lines[line] === undefined) {
|
|
|
+ marked_lines[line] = true;
|
|
|
+
|
|
|
+ var Range = require("ace/range").Range;
|
|
|
+ var line_range = new Range(line, 0, line + 1, 0);
|
|
|
+
|
|
|
+ var marker = editor.getSession().addMarker(line_range, "ace_error", "line", false);
|
|
|
+ markers.push(marker);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ editor.getSession().setAnnotations(annos);
|
|
|
+ }
|
|
|
+
|
|
|
+ $("#status_text").html(text);
|
|
|
+
|
|
|
+ g_sample_count = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ $("#editor").keyup(function() {
|
|
|
+ clearTimeout(timeout);
|
|
|
+ timeout = setTimeout(rebuild, 500);
|
|
|
+ });
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+</body>
|
|
|
+</html>
|