Merge pull request #1 from sunyinqi0508/hw2

Hw2
master
sunyinqi0508 3 years ago committed by GitHub
commit e87f15d2a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 336 KiB

BIN
2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 KiB

@ -1,4 +1,4 @@
<script src=lib1.js></script>
<script src=lib2.js></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ext-language_tools.js" crossorigin="anonymous"></script>
<style>
@ -21,7 +21,7 @@
</style>
<body bgcolor=white text=black link=black alink=blue vlink=blue>
<center>
<canvas id='canvas1' width=600 height=600></canvas>
<canvas id='canvas1' style="width: 600px; height:600px;" width=1200 height=1200></canvas>
</center>
</body>
@ -39,197 +39,22 @@
<!!-------- FRAGMENT SHADER: THIS IS WHERE YOU WILL DO YOUR WORK -------->
<script id='my_fragment_shader' type='x-shader/x-fragment'>
uniform float uTime; // TIME, IN SECONDS
uniform int flags;
varying vec3 vPos; // -1 < vPos.x < +1
// -1 < vPos.y < +1
// vPos.z == 0
uniform sampler2D uSampler;
// YOU MUST DEFINE A main() FUNCTION.
void main() {
////////////////////////////////////////////////
//
// HERE, FOR YOUR HOMEWORK, YOU CAN WRITE ANY
// CODE YOU LIKE
//DEFINE A COLOR FOR THIS FRAGMENT.
// LIGHT DIRECTION AND COLOR
vec3 LDir = vec3(1, 1, 1);
vec3 LCol = vec3(1.,.98,.9);
vec3 VDir = vec3(0., 0., 1.);
float R = 0.5 + 0.3*sin(uTime/10.);
// vPos IS THE 3D LOCATION OF THIS FRAGMENT.
// SURFACE REFLECTANCE PROPERTIES
vec3 diffuse = vec3(.7,.7,.6);
vec3 ambient = vec3(.1,.1,.09);
vec3 specular = vec3(.35,.35,.3);
// CREATE A WACKY BACKGROUND PATTERN.
vec3 color = vec3(.5 + .5 * sin(20. * vPos.x + sin(10. * vPos.y + 5. * uTime)),
.5,
.5 + .5 * sin(20. * vPos.y + sin(10. * vPos.x + 5. * uTime)));
vec3 specularlight; //specular light should disregard object color.
float x = vPos.x;
float y = vPos.y;
// CREATE BOUNCING ANIMATION.
y += .5 - 2. * abs(.5 * sin(uTime));
// FIGURE OUT WHETHER THIS PIXEL IS ON THE SPHERE.
float rr = x * x + y * y;
// IF SO, THEN SHADE THE SPHERE.
if (rr < R*R) {
// COMPUTE THE z AND NORMAL OF THE SPHERE.
float z = R * sqrt(1. - rr/(R*R));
vec3 N = vec3(x,y,z) / R;
// APPLY SHADING AND LIGHTING.
vec3 realVPos = vec3(vPos.x, vPos.y, z);
//*DIRECTIONS ARE NORMALIZED TO GET THE CORRECT PHONG LIGHTING
LDir = normalize(LDir - realVPos);
VDir = normalize(VDir - realVPos);
vec3 lcolor = ambient;
lcolor += diffuse * max(0., dot(N, LDir)) * LCol;
//*SPECULAR LIGHTS ARE ADDED
specularlight = specular *
pow(max(0.,dot(2.*dot(N, LDir) * N - LDir, VDir)),3.) * LCol;
// ROTATE THE TEXTURE OVER TIME.
float angle = 0.4 * uTime;
float px = cos(angle) * N.x + sin(angle) * N.z;
float pz = -sin(angle) * N.x + cos(angle) * N.z;
vec3 P = vec3(px, N.y, pz);
// APPLY PROCEDURAL NOISE TEXTURE.
float cloud = min(0.85, max(-0.05, 2. * noise(1.1 * P)));
//const float rspeed = 10.;
//*CALCULATING THE TEXTURE COORDINATE.
const float pi = 3.14159265359;
float tex_x = acos(abs(x)/sqrt(R*R-y*y));
if(x > 0.)
tex_x = pi - tex_x;
tex_x = R * tex_x;
tex_x *= 1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
tex_x = tex_x + float(uTime)*R;
float _2pir = 2. * pi * R;
float quo = float(int(tex_x/_2pir));
tex_x = (tex_x - quo * _2pir) / _2pir;
//*TEXTURE MAPPING
vec3 texture_color;
if(flags /*& 0x1*/ == 0)
texture_color = texture2D(uSampler, vec2(tex_x, ((R - y)/(2.*R)))).xyz;
else
texture_color = vec3(.0841, .5329, .9604);
color = lcolor *
(cloud + texture_color) + specularlight;
}
//*CALCULATING LIGHTING AND SHADOWS FOR THE BACKGROUND
else{
vec3 realVPos = vec3(vPos.x, vPos.y, -1.);
vec3 N = vec3(0., 0., 1.);
vec3 C = vec3(0., vPos.y - y, 0.);
// APPLY SHADING AND LIGHTING.
vec3 lcolor = 6.*ambient;
//*CALCULATING DISTANCE BETWEEN SPHERE CENTER TO THE RAY
//BETWEEN THE POINT TO LIGHT SOURCE
vec3 LV = realVPos - LDir;
vec3 LC = C - LDir;
vec3 VC = C - realVPos;
float lLV = dot(LV, LV);
float lLC = dot(LC, LC);
float lVC = dot(VC, VC);
float RR = R*R;
float d_VCVL = -dot(VC, LV);
float dist = min(lLC, lVC);
if (d_VCVL > 0.)
dist = min(dist, lVC - d_VCVL*d_VCVL/lLV);
//*CALCULATE DISTANCE BETWEEN SPHERE CENTER TO THE RAY BETWEEN POINT TO CAMERA
vec3 EV = realVPos - VDir;
vec3 EC = C - VDir;
float lEV = dot(EV, EV);
float lEC = dot(EC, EC);
float d_VCVE = -dot(VC, EV);
float dist2 = min(lEC, lVC);
if (d_VCVE > 0.)
dist2 = min(dist2, lVC - d_VCVE*d_VCVE/lEV);
//*AMBIENT LIGHT WILL DECAY WHEN BACKGROUND POINT IS CLOSER TO THE SPHERE
//*FIRST THE DISTANCE IS NORMALIZED, THEN I CURVED IT WITH LOGISTIC FUNCTION
float aratio0 = 1./(1.+pow(2.71828,15.*(.2-(sqrt(lVC) - R - .4806)/1.381)));
lcolor *= aratio0;
diffuse *= pow(aratio0, 0.3);
//*TEST IF CAMERA CAN SEE THE POINT
if(dist2 < RR)
{
specular *= 0.;
float d = sqrt(dist2);
float ratio = pow(2., sqrt(dist2)/R) - 1.;
diffuse *= ratio;
}
//*TEST IF THE LIGHT CAN REACH THE BACKGROUND POINT DIRECTLY
if(dist < RR)
{
specular *= 0.;
float ratio = pow(2.7, sqrt(dist)/R) - 1.7;
float aratio = pow(2., sqrt(dist)/R) - 1.;
if(ratio < 0.)
ratio = 0.;
diffuse *= ratio;
lcolor *= aratio;
}
LDir = normalize(LDir - realVPos);
VDir = normalize(VDir - realVPos);
lcolor += 1.2*diffuse * max(0., dot(N, LDir)) * LCol;
specularlight = 0.3*specular *
pow(max(0., dot(2.*dot(N, LDir) * N - LDir, VDir)),32.) * LCol;
color = color * lcolor + specularlight;
}
// APPLY GAMMA CORRECTION AND SET THE PIXEL COLOR.
gl_FragColor = vec4(sqrt(color), 1.0);
}
</script>
<!!-------- FRAGMENT SHADER: MOVED TO ./shader.frag LOADED IN lib2.js -------->
<!--script src="shader.frag" id='my_fragment_shader' type='x-shader/x-fragment'> </script>
<!!-------- CREATE A PROPERLY DESCRIPTIVE TITLE BELOW -------->
<script id='my_title' type='text/html'>
The Bouncing Earth
Solar RTX
</script>
<!!-------- HERE IS WHERE YOU CAN PROVIDE A LONGER TEXT DESCRIPTION -------->
<script id='my_instructions' type='text/html'><font color=#909090>
<p style="font-size:30px; ">This is a description
of my cool homework
that you are seeing now.</p>
<p style="font-size:30px; ">In this homework, I implemented Global illumination w/
Realtime Recursive Ray Tracing!
<p>
<i style="font-size:25px;">Here is how it works:</i>
<ul>
@ -245,7 +70,7 @@ that you are seeing now.</p>
<ul>
<li>The Ambient lights and diffusion lights are reduced with respect to the distance between the background point and the sphere.</li>
<li>The specular light of the background wall is eliminated and the diffusion factor is reduced when the ray shooting from the background point
towards the light source or from the background point towards the camera position intersect with the sphere.</li>
towards the light source or from the background point towards the camera position intersect with the sphere.</li>
</ul>
<li>I added basic interactions such as press ctrl + 't' key to hide/show texture, click on the above canvas to pause/unpause animations.
Just a proof of concept.</li>
@ -260,16 +85,30 @@ that you are seeing now.</p>
</script>
<!!-------- YOU PROBABLY DON'T WANT TO CHANGE ANYTHING BELOW FOR NOW -------->
<!!-------- YOU PROBABLY WANT TO CHANGE ANYTHING BELOW RIGHT NOW -------->
<script>
// CREATE THE HTML DOCUMENT
let flags = 0x0;
let vs = my_vertex_shader.innerHTML,
fs = my_fragment_shader.innerHTML;
fs = fs.substring(1, fs.length);
let vs = my_vertex_shader.innerHTML;
//* LOADING FRAGMENT SHADER
var client = new XMLHttpRequest();
client.open('GET', './shader.frag');
client.onloadend = function() {
fs = (client.responseText);
//* START EVERYTHING AFTER FRAGMENT SHADER IS DOWNLOADED.
gl_start(canvas1, vs, fs);
editor.getSession().setValue(fs);
editor.session.on('change', function(delta) {
if(typeof canvas1.setShaders === "function")
{
canvas1.setShaders(vs, editor.getSession().getValue());
setUniform('1i', 'flags', flags);
}
});
}
client.send();
document.body.innerHTML = [''
,'<font size=7 color=#909090>' + my_title.innerHTML
,'<TABLE cellspacing=0 cellpadding=0><TR>'
@ -285,10 +124,6 @@ document.body.innerHTML = [''
].join('');
// SET UP THE EDITABLE TEXT AREA ON THE LEFT SIDE.
let text = fs.split('\n'), cols = 0;
for (let i = 0 ; i < text.length ; i++)
cols = Math.max(cols, text[i].length);
ace.require("ace/ext/language_tools");
var editor = ace.edit("ace", {
mode:"ace/mode/glsl",
@ -305,12 +140,8 @@ editor.setOptions({
showPrintMargin: false,
});
editor.setAutoScrollEditorIntoView(true);
editor.getSession().setValue(fs);
// REPARSE THE SHADER PROGRAM AFTER EVERY KEYSTROKE.
editor.session.on('change', function(delta) {
canvas1.setShaders(vs, editor.getSession().getValue());
});
delete editor.KeyBinding;
let lastTime = Date.now();
let animating = true;
let ctrl = false;
@ -322,17 +153,23 @@ canvas1.addEventListener('click',function(ev){
animating = !animating;
});
document.addEventListener('keydown',(e)=>{
if(e.code.startsWith('Control'))
{
ctrl = true;
}
else if(ctrl && e.code == 'KeyT')
if(e.code.startsWith('Control'))
{
ctrl = true;
}
else if(ctrl && e.code == 'KeyT')
{
mask = 0x1;
flags = flags&!mask | (!(flags&mask)*mask);
flags = flags&!mask | (!(flags&mask)?mask:0);
setUniform('1i', 'flags', flags);
}
else if (ctrl && e.code == 'KeyS')
{
let a = document.createElement('a');
a.href = "data:text/plain,"+encodeURIComponent(editor.getSession().getValue());
a.download = 'shader.frag';
a.click();
}
});
document.addEventListener('keyup',(e)=>{
@ -353,8 +190,6 @@ function animate(gl) {
}
// START EVERYTHING.
gl_start(canvas1, vs, fs);
</script>

@ -24,15 +24,17 @@ let errorMsg = '';
// Initialize a texture and load an image.
// When the image finished loading copy it into the texture.
//
function loadTexture(gl, url) {
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Because images have to be downloaded over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
function getBlob(data) {
let bytes = new Array(data.length);
for (let i = 0; i < data.length; i++) {
bytes[i] = data.charCodeAt(i);
}
return new Blob([new Uint8Array(bytes)]);
}
let texture = [], gl, program;
let textures = [];
let lock = false;
function loadTexture(gl, url, i) {
const level = 0;
const internalFormat = gl.RGBA;
const width = 1;
@ -40,23 +42,36 @@ function loadTexture(gl, url) {
const border = 0;
const srcFormat = gl.RGBA;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
if (texture[i] == null)
{
texture[i] = gl.createTexture();
const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue
gl.activeTexture(gl.TEXTURE0+i);
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
width, height, border, srcFormat, srcType,
pixel);
}
// Because images have to be downloaded over the internet
// they might take a moment until they are ready.
// Until then put a single pixel in the texture so we can
// use it immediately. When the image has finished downloading
// we'll update the texture with the contents of the image.
const image = new Image();
image.onload = function () {
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.activeTexture(gl.TEXTURE0+i);
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
srcFormat, srcType, image);
// WebGL1 has different requirements for power of 2 images
// vs non power of 2 images so check if the image is a
// power of 2 in both dimensions.
if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
// Yes, it's a power of 2. Generate mips.
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
} else {
// No, it's not a power of 2. Turn off mips and set
// wrapping to clamp to edge
@ -66,8 +81,6 @@ function loadTexture(gl, url) {
}
};
image.src = url;
return texture;
}
function isPowerOf2(value) {
@ -77,12 +90,13 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
setTimeout(function () {
try {
canvas.gl = canvas.getContext('experimental-webgl'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD.
canvas.gl = canvas.getContext('webgl2'); // Make sure WebGl is supported. IT WOULD BE GREAT TO USE WEBGL2 INSTEAD.
} catch (e) { throw 'Sorry, your browser does not support WebGL.'; }
canvas.setShaders = function (vertexShader, fragmentShader) { // Add the vertex and fragment shaders:
let gl = this.gl, program = gl.createProgram(); // Create the WebGL program.
gl = this.gl;
program = gl.createProgram(); // Create the WebGL program.
function addshader(type, src) { // Create and attach a WebGL shader.
function spacer(color, width, height) {
@ -131,15 +145,12 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
console.log('Could not link the shader program!');
gl.useProgram(program);
gl.program = program;
let texture = loadTexture(gl, './earthmap1k.jpg') //Texture loading.
// Tell WebGL we want to affect texture unit 0
gl.activeTexture(gl.TEXTURE0);
// Bind the texture to texture unit 0
gl.bindTexture(gl.TEXTURE_2D, texture);
// Tell the shader we bound the texture to texture unit 0
gl.uniform1i(gl.getUniformLocation(program, 'uSampler'), 0);
const ns = 2;
for(let i = 0; i < ns; ++i){
loadTexture(gl, './'+(i+1)+'.jpg', i); //Texture loading.
textures[i] = i;
}
gl.uniform1iv(gl.getUniformLocation(program, 'uSampler'), textures);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); // Create a square as a triangle strip
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( // consisting of two triangles.
@ -151,7 +162,6 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
}
canvas.setShaders(vertexShader, fragmentShader); // Initialize everything,
setInterval(function () { // Start the animation loop.
gl = canvas.gl;
if (gl.startTime === undefined) // First time through,
@ -167,7 +177,6 @@ function gl_start(canvas, vertexShader, fragmentShader) { // START WEB
function animate() { }
let gl;
function setUniform(type, name, a, b, c, d, e, f) {
let loc = gl.getUniformLocation(gl.program, name);
(gl['uniform' + type])(loc, a, b, c, d, e, f);

@ -0,0 +1,197 @@
vec3 foregroundColor = vec3(.0841, .5329, .9604);
vec3 groundColor = vec3(.2, .3, .5);
vec4 groundSpecular = vec4(.71, .71, .71, 10.);
uniform float uTime;// TIME, IN SECONDS
uniform int flags;
//FLAGS 0-RT, 1-TEX, 2-
varying vec3 vPos;// -1 < vPos.x < +1
// -1 < vPos.y < +1
// vPos.z == 0
float fl=3.;
const float pi=3.14159265359;
const int n_ref=2;
const int ns=2;
vec4 Sph[ns];
uniform sampler2D uSampler[ns];
vec3 Ambient[ns];
vec3 Diffuse[ns];
vec4 Specular[ns];
struct RT{
vec3 color;
float ks;
// vec3 ptr;
// vec3 normal;
} stack[n_ref];
bool getflag(int flag,int bit){
float shifted = float(int(float(flag)/ pow(2.,float(bit))));
return fract(shifted/2.)>0.;
}
float clampv(float val,float l,float h){
return val<l?l:val>h?h:val;
}
void main(){
////////////////////////////////////////////////
//
// HERE, FOR YOUR HOMEWORK, YOU CAN WRITE ANY
// CODE YOU LIKDEFINE A COLOR FOR THIS FRAGMENT.
// LIGHT DIRECTION AND COLOR
vec3 LDir=vec3(.5,.5,.5);
vec3 LCol=vec3(1.,1.,1.);
// SPHERE
Sph[0]=vec4(.5*sin(uTime),0.,.5*cos(uTime),.2);
Sph[1]=vec4(0.,0.,0.,.2);
// SURFACE REFLECTANCE PROPERTIES
Ambient[1]=vec3(.1,.05,.05);// r,g,b
Diffuse[1]=vec3(1.,.5,.5);// r,g,b
Specular[1]=vec4(1.,.5,.5,10.);// r,g,b,power
Ambient[0]=vec3(.05,.05,.1);// r,g,b
Diffuse[0]=vec3(.5,.5,1.);// r,g,b
Specular[0]=vec4(1.,.5,.5,20.);// r,g,b,power
// INITIALIZE TO A BACKGROUND COLOR
vec3 color=vec3(.2, .3, .5);
// COMPUTE THE RAY ORIGIN AND DIRECTION
float x=vPos.x;
float y=vPos.y;
vec3 V=vec3(0.,0.,fl);
vec3 W=normalize(vec3(x,y,-fl));
// RAY TRACE TO ALL OBJECTS IN THE SCENE
bool rtxoff = getflag(flags, 1);
int cnt_ref = n_ref;
for(int j=0;j<n_ref;j++)
{
float tMin=10000.;
int iMin = -1;
for(int i=0;i<ns;i++){
// SHIFT COORDINATES, SO THAT SPHERE IS AT (0,0,0)
vec3 Vp=V-Sph[i].xyz;
// SOLVE FOR QUADRATIC EQUATION IN t
float B=dot(W,Vp);
float C=dot(Vp,Vp)-Sph[i].w*Sph[i].w;
float D=B*B-C;
if(D>0.){
float t=-B-sqrt(D);
if(t > 0. && t < tMin){
tMin = t;
iMin = i;
}
}
}
// IF RAY HITS SPHERE
if(iMin >= 0){
float t = tMin;
vec3 S=V+t*W;
for(int i = 0; i < ns; ++ i)
if(i == iMin)
{
//*TEXTURE MAPPING
vec3 tex_sph=S-Sph[i].xyz;
float R=Sph[i].w;
float tex_x=acos(abs(tex_sph.x)/sqrt(R*R-tex_sph.y*tex_sph.y));
if(tex_sph.x>0.)
tex_x=pi-tex_x;
tex_x=R*tex_x;
tex_x*=1.5708;//*Correct aspect ratio of texture 2:1 -> 2pir:2r
tex_x=tex_x+float(uTime)*R;
float _2pir=2.*pi*R;
float quo=float(int(tex_x/_2pir));
tex_x=clampv((tex_x-quo*_2pir),0.,_2pir)/_2pir;
vec3 texture_color;
if(!getflag(flags,0))
texture_color=texture2D(uSampler[i],vec2(tex_x,((R-tex_sph.y)/(2.*R)))).xyz;
else texture_color = foregroundColor;
vec3 N=normalize(S-Sph[i].xyz);
//*DIRECTIONS ARE NORMALIZED TO GET THE CORRECT PHONG LIGHTING
vec3 realLDir=normalize(LDir-S);
color=(
Ambient[i]
+Diffuse[i]*max(0.,dot(N,realLDir))*LCol
)*texture_color
;
// + SPECULAR COMPONENT GOES HERE
if(rtxoff || j == n_ref - 1)
color += float(j) * Specular[i].xyz*pow(max(0.,dot(2.*dot(N,realLDir)*N-realLDir,-W)),Specular[i].w);
stack[j] = RT(color, 0.15);
V = S;
W = -normalize(2. * dot(N, W) * N - W);
break;
}
}
else {
// TO SIMIPIFY THINGS UP, I'LL ASSUME THAT EVERYTHING
// IS INSIDE THE BOUNDING BOX [(-1,-1,-1), (1,1,1)]
// AND THERE'S A INFINITE FLOOR [y = -1]
float t = -(.2+V.y)/W.y;
float sx = V.x + t* W.x, sz = V.z + t * W.z;
if(t >= 0.&&abs(sx)<1.5 && abs(sz+.6)<3.)
{
vec3 S = vec3(sx, -.2, sz);
vec3 realLDir=normalize(LDir - S);
color=(
0.5
+0.5*max(0.,realLDir.y)*LCol
)*groundColor
;
// + SPECULAR COMPONENT GOES HERE
if(rtxoff || j == n_ref - 1)
color += float(j)*groundSpecular.xyz*
pow(max(0., dot(vec3(-realLDir.x, realLDir.y,-realLDir.z),-W)),groundSpecular.w);
stack[j] = RT(color, 0.1);
V = S;
W = vec3(W.x, -W.y, W.z);
}
else{
if(j > 0)
{
stack[j] = RT(vec3(12.,12.,12.)*pow(max(0.,dot(W, normalize(LDir - V))), 10.), 0.);
cnt_ref = j + 1;
}
else
cnt_ref = j;
break;
}
}
// RTX off
if(rtxoff)
break;
}
if(rtxoff)
color = stack[0].color;
else
{
color = vec3(0,0,0);
float currks = 1.;
for(int i = 0; i < n_ref; ++i)
{
if(i >= cnt_ref)
{
color += currks * stack[i - 1].color;
break;
}
color += currks *(1.-stack[i].ks) * stack[i].color;
currks *= stack[i].ks;
}
if(n_ref == cnt_ref)
color += currks * stack[n_ref - 1].color;
}
// APPLY GAMMA CORRECTION AND SET THE PIXEL COLOR.
gl_FragColor=vec4(sqrt(color),1.);
}
Loading…
Cancel
Save