diff --git a/README.md b/README.md index 06fcfd4..2b4497d 100644 --- a/README.md +++ b/README.md @@ -3,371 +3,78 @@ CIS565: Project 5: WebGL ------------------------------------------------------------------------------- Fall 2013 ------------------------------------------------------------------------------- -Due Friday 11/08/2013 +Yingting Xiao ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -NOTE: +PART 1 FEATURES: ------------------------------------------------------------------------------- -This project requires any graphics card with support for a modern OpenGL -pipeline. Any AMD, NVIDIA, or Intel card from the past few years should work -fine, and every machine in the SIG Lab and Moore 100 is capable of running -this project. -This project also requires a WebGL capable browser. The project is known to -have issues with Chrome on windows, but Firefox seems to run it fine. +Sine wave: -------------------------------------------------------------------------------- -INTRODUCTION: -------------------------------------------------------------------------------- -In this project, you will get introduced to the world of GLSL in two parts: -vertex shading and fragment shading. The first part of this project is the -Image Processor, and the second part of this project is a Wave Vertex Shader. - -In the first part of this project, you will implement a GLSL vertex shader as -part of a WebGL demo. You will create a dynamic wave animation using code that -runs entirely on the GPU. - -In the second part of this project, you will implement a GLSL fragment shader -to render an interactive globe in WebGL. This will include texture blending, -bump mapping, specular masking, and adding a cloud layer to give your globe a -uniquie feel. - -------------------------------------------------------------------------------- -CONTENTS: -------------------------------------------------------------------------------- -The Project4 root directory contains the following subdirectories: - -* part1/ contains the base code for the Wave Vertex Shader. -* part2/ contains the base code for the Globe Fragment Shader. -* resources/ contains the screenshots found in this readme file. - -------------------------------------------------------------------------------- -PART 1 REQUIREMENTS: -------------------------------------------------------------------------------- - -In Part 1, you are given code for: - -* Drawing a VBO through WebGL -* Javascript code for interfacing with WebGL -* Functions for generating simplex noise - -You are required to implement the following: - -* A sin-wave based vertex shader: - -![Example sin wave grid](resources/sinWaveGrid.png) - -* A simplex noise based vertex shader: - -![Example simplex noise wave grid](resources/oceanWave.png) - -* One interesting vertex shader of your choice +[![Sine wave](screenshots/sinewave.PNG)](http://yingtingxiao.github.io/Project5-WebGL/vert_wave.html) -------------------------------------------------------------------------------- -PART 1 WALKTHROUGH: -------------------------------------------------------------------------------- -**Sin Wave** - -* For this assignment, you will need the latest version of Firefox. -* Begin by opening index.html. You should see a flat grid of black and white - lines on the xy plane: - -![Example boring grid](resources/emptyGrid.png) - -* In this assignment, you will animate the grid in a wave-like pattern using a - vertex shader, and determine each vertex’s color based on its height, as seen - in the example in the requirements. -* The vertex and fragment shader are located in script tags in `index.html`. -* The JavaScript code that needs to be modified is located in `index.js`. -* Required shader code modifications: - * Add a float uniform named u_time. - * Modify the vertex’s height using the following code: - - ```glsl - float s_contrib = sin(position.x*2.0*3.14159 + u_time); - float t_contrib = cos(position.y*2.0*3.14159 + u_time); - float height = s_contrib*t_contrib; - ``` - - * Use the GLSL mix function to blend together two colors of your choice based - on the vertex’s height. The lowest possible height should be assigned one - color (for example, `vec3(1.0, 0.2, 0.0)`) and the maximum height should be - another (`vec3(0.0, 0.8, 1.0)`). Use a varying variable to pass the color to - the fragment shader, where you will assign it `gl_FragColor`. - -* Required JavaScript code modifications: - * A floating-point time value should be increased every animation step. - Hint: the delta should be less than one. - * To pass the time to the vertex shader as a uniform, first query the location - of `u_time` using `context.getUniformLocation` in `initializeShader()`. - Then, the uniform’s value can be set by calling `context.uniform1f` in - `animate()`. - -**Simplex Wave** - -* Now that you have the sin wave working, create a new copy of `index.html`. - Call it `index_simplex.html`, or something similar. -* Open up `simplex.vert`, which contains a compact GLSL simplex noise - implementation, in a text editor. Copy and paste the functions included - inside into your `index_simplex.html`'s vertex shader. -* Try changing s_contrib and t_contrib to use simplex noise instead of sin/cos - functions with the following code: - -```glsl -vec2 simplexVec = vec2(u_time, position); -float s_contrib = snoise(simplexVec); -float t_contrib = snoise(vec2(s_contrib,u_time)); -``` - -**Wave Of Your Choice** - -* Create another copy of `index.html`. Call it `index_custom.html`, or - something similar. -* Implement your own interesting vertex shader! In your README.md with your - submission, describe your custom vertex shader, what it does, and how it - works. - -------------------------------------------------------------------------------- -PART 2 REQUIREMENTS: -------------------------------------------------------------------------------- -In Part 2, you are given code for: +--- -* Reading and loading textures -* Rendering a sphere with textures mapped on -* Basic passthrough fragment and vertex shaders -* A basic globe with Earth terrain color mapping -* Gamma correcting textures -* javascript to interact with the mouse - * left-click and drag moves the camera around - * right-click and drag moves the camera in and out +Simplex wave 1D: -You are required to implement: +[![Simplex wave1](screenshots/simplexwave1.PNG)](http://yingtingxiao.github.io/Project5-WebGL/vert_wave_simplex.html) -* Bump mapped terrain -* Rim lighting to simulate atmosphere -* Night-time lights on the dark side of the globe -* Specular mapping -* Moving clouds +--- -You are also required to pick one open-ended effect to implement: +Simplex wave 2D: -* Procedural water rendering and animation using noise -* Shade based on altitude using the height map -* Cloud shadows via ray-tracing through the cloud map in the fragment shader -* Orbiting Moon with texture mapping and shadow casting onto Earth -* Draw a skybox around the entire scene for the stars. -* Your choice! Email Liam and Patrick to get approval first +[![Simplex wave2](screenshots/simplexwave2.PNG)](http://yingtingxiao.github.io/Project5-WebGL/vert_wave_custom.html) -Finally in addition to your readme, you must also set up a gh-pages branch -(explained below) to expose your beautiful WebGL globe to the world. +Instead of passing vec2(0.1*u_time, position) to simplexNoise, I pass vec2(position.x+0.25*u_time, position.y+0.25*u_time) to it. As a result, I get a nice 2D simplex wave. -Some examples of what your completed globe renderer will look like: +--- -![Completed globe, day side](resources/globe_day.png) +Perlin wave 2D: -Figure 0. Completed globe renderer, daylight side. +[![Perlin wave](screenshots/perlinwave.PNG)](http://yingtingxiao.github.io/Project5-WebGL/vert_wave_perlin.html) -![Completed globe, twilight](resources/globe_twilight.png) +I followed the instructions on http://freespace.virgin.net/hugo.elias/models/m_perlin.htm to write my 2D perlin noise generator. -Figure 1. Completed globe renderer, twilight border. +Since WebGL doesn't support bitwise operations (used in the perlin noise tutorial for generating random numbers) or GLSL noise functions, I use this function to generate a pseudo-random number between 0 and 1 (credit: http://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl/4275343#4275343): -![Completed globe, night side](resources/globe_night.png) +float noise(float x, float y) { + return fract(sin(dot(vec2(x,y) ,vec2(12.9898,78.233))) * 43758.5453); +} -Figure 2. Completed globe renderer, night side. +It works perfectly well! For getting the perlin noise at a specific position I simply call perlin on vec2(position.x+0.25*u_time, position.y). ------------------------------------------------------------------------------- -PART 2 WALKTHROUGH: +PART 2 FEATURES: ------------------------------------------------------------------------------- -Open part2/frag_globe.html in Firefox to run it. You’ll see a globe -with Phong lighting like the one in Figure 3. All changes you need to make -will be in the fragment shader portion of this file. - -![Initial globe](resources/globe_initial.png) - -Figure 3. Initial globe with diffuse and specular lighting. - -**Night Lights** - -The backside of the globe not facing the sun is completely black in the -initial globe. Use the `diffuse` lighting component to detect if a fragment -is on this side of the globe, and, if so, shade it with the color from the -night light texture, `u_Night`. Do not abruptly switch from day to night; -instead use the `GLSL mix` function to smoothly transition from day to night -over a reasonable period. The resulting globe will look like Figure 4. -Consider brightening the night lights by multiplying the value by two. - -The base code shows an example of how to gamma correct the nighttime texture: - -```glsl -float gammaCorrect = 1/1.2; -vec4 nightColor = pow(texture2D(u_Night, v_Texcoord), vec4(gammaCorrect)); -``` - -Feel free to play with gamma correcting the night and day textures if you -wish. Find values that you think look nice! - -![Day/Night without specular mapping](resources/globe_nospecmap.png) - -Figure 4. Globe with night lights and day/night blending at dusk/dawn. - -**Specular Map** +All required features: -Our day/night color still shows specular highlights on landmasses, which -should only be diffuse lit. Only the ocean should receive specular highlights. -Use `u_EarthSpec` to determine if a fragment is on ocean or land, and only -include the specular component if it is in ocean. - -![Day/Night with specular mapping](resources/globe_specmap.png) - -Figure 5. Globe with specular map. Compare to Figure 4. Here, the specular -component is not used when shading the land. - -**Clouds** - -In day time, clouds should be diffuse lit. Use `u_Cloud` to determine the -cloud color, and `u_CloudTrans` and `mix` to determine how much a daytime -fragment is affected by the day diffuse map or cloud color. See Figure 6. - -In night time, clouds should obscure city lights. Use `u_CloudTrans` and `mix` -to blend between the city lights and solid black. See Figure 7. - -Animate the clouds by offseting the `s` component of `v_Texcoord` by `u_time` -when reading `u_Cloud` and `u_CloudTrans`. - -![Day with clouds](resources/globe_daycloud.png) - -Figure 6. Clouds with day time shading. - -![Night with clouds](resources/globe_nightcloud.png) - -Figure 7. Clouds observing city nights on the dark side of the globe. - -**Bump Mapping** - -Add the appearance of mountains by perturbing the normal used for diffuse -lighting the ground (not the clouds) by using the bump map texture, `u_Bump`. -This texture is 1024x512, and is zero when the fragment is at sea-level, and -one when the fragment is on the highest mountain. Read three texels from this -texture: once using `v_Texcoord`; once one texel to the right; and once one -texel above. Create a perturbed normal in tangent space: - -`normalize(vec3(center - right, center - top, 0.2))` - -Use `eastNorthUpToEyeCoordinates` to transform this normal to eye coordinates, -normalize it, then use it for diffuse lighting the ground instead of the -original normal. - -![Globe with bump mapping](resources/globe_bumpmap.png) - -Figure 8. Bump mapping brings attention to mountains. - -**Rim Lighting** - -Rim lighting is a simple post-processed lighting effect we can apply to make -the globe look as if it has an atmospheric layer catching light from the sun. -Implementing rim lighting is simple; we being by finding the dot product of -`v_Normal` and `v_Position`, and add 1 to the dot product. We call this value -our rim factor. If the rim factor is greater than 0, then we add a blue color -based on the rim factor to the current fragment color. You might use a color -something like `vec4(rim/4, rim/2, rim/2, 1)`. If our rim factor is not greater -than 0, then we leave the fragment color as is. Figures 0,1 and 2 show our -finished globe with rim lighting. +[![Globe](screenshots/globe.PNG)](http://yingtingxiao.github.io/Project5-WebGL/frag_globe.html) +--- -For more information on rim lighting, -read http://www.fundza.com/rman_shaders/surface/fake_rim/fake_rim1.html. +Additional feature: -------------------------------------------------------------------------------- -GH-PAGES -------------------------------------------------------------------------------- -Since this assignment is in WebGL you will make your project easily viewable by -taking advantage of GitHub's project pages feature. +Procedural water rendering and animation using noise: -Once you are done you will need to create a new branch named gh-pages: +Version 1: -`git branch gh-pages` +[![Globe with water1](screenshots/water1.PNG)](http://yingtingxiao.github.io/Project5-WebGL/frag_globe_perlin.html) -Switch to your new branch: +I turn off clouds in order to show the animation of water more clearly. In this version I use perlin noise as a bump map. I use the perlin noise values of a pixel's right and top points on the globe to compute the normal. -`git checkout gh-pages` +Version 2: -Create an index.html file that is either your renamed frag_globe.html or -contains a link to it, commit, and then push as usual. Now you can go to +[![Globe with water2](screenshots/water2.PNG)](http://yingtingxiao.github.io/Project5-WebGL/frag_globe_perlin_vert.html) -`.github.io/` +Since version 1 doesn't look very realistic, I tried a different approach: using perlin noise as a displacement map in the vertex shader. However, displacing the vertices only has an effect on the edge of the globe. In order to make the center of the globe look bumpy, I have to change the normals of vertices in the vertex shader. -to see your beautiful globe from anywhere. +In the vertex shader, I compute the spherical coordinates of the vertex, get the perlin noise value with vec2(theta, phi), and move the vertex along its normal by the perlin noise value. Then I add 0.001 to phi and get the displaced position at the new spherical coordinates. For theta I do the same thing. Finally I use the positions of the three points to compute the normal at the vertex. -------------------------------------------------------------------------------- -README -------------------------------------------------------------------------------- -All students must replace or augment the contents of this Readme.md in a clear -manner with the following: - -* A brief description of the project and the specific features you implemented. -* At least one screenshot of your project running. -* A 30 second or longer video of your project running. To create the video you - can use http://www.microsoft.com/expression/products/Encoder4_Overview.aspx -* A performance evaluation (described in detail below). +This method gives me nice bumpy water on 1/4 of the globe, as shown in the image. However, the rest of the water looks really dark. I think the part on the sphere that looks nice is the part where phi and theta are both positive. I think the incorrect normals is probably due to my operations on the spherical coordinates. There might be something wrong in the way I convert cartesian coordinates to spherical coordinates, because phi is supposed to be in [0, 2*pi) and atan returns a value in (-0.5*pi, 0.5*pi). It was really hard to debug this, since GLSL doesn't have its debugger and the brightness of the global changes with the camera. I tried a bunch of different operations on spherical coordinates but couldn't get it to work right. If I have time to work on this, I will think about the math and try to figure out what the spherical coordinates are at different parts of the globe. ------------------------------------------------------------------------------- PERFORMANCE EVALUATION ------------------------------------------------------------------------------- -The performance evaluation is where you will investigate how to make your -program more efficient using the skills you've learned in class. You must have -performed at least one experiment on your code to investigate the positive or -negative effects on performance. - -We encourage you to get creative with your tweaks. Consider places in your code -that could be considered bottlenecks and try to improve them. -Each student should provide no more than a one page summary of their -optimizations along with tables and or graphs to visually explain any -performance differences. - -------------------------------------------------------------------------------- -THIRD PARTY CODE POLICY -------------------------------------------------------------------------------- -* Use of any third-party code must be approved by asking on the Google groups. - If it is approved, all students are welcome to use it. Generally, we approve - use of third-party code that is not a core part of the project. For example, - for the ray tracer, we would approve using a third-party library for loading - models, but would not approve copying and pasting a CUDA function for doing - refraction. -* Third-party code must be credited in README.md. -* Using third-party code without its approval, including using another - student's code, is an academic integrity violation, and will result in you - receiving an F for the semester. - -------------------------------------------------------------------------------- -SELF-GRADING -------------------------------------------------------------------------------- -* On the submission date, email your grade, on a scale of 0 to 100, to Liam, - liamboone@gmail.com, with a one paragraph explanation. Be concise and - realistic. Recall that we reserve 30 points as a sanity check to adjust your - grade. Your actual grade will be (0.7 * your grade) + (0.3 * our grade). We - hope to only use this in extreme cases when your grade does not realistically - reflect your work - it is either too high or too low. In most cases, we plan - to give you the exact grade you suggest. -* Projects are not weighted evenly, e.g., Project 0 doesn't count as much as - the path tracer. We will determine the weighting at the end of the semester - based on the size of each project. - - ---- -SUBMISSION ---- -As with the previous project, you should fork this project and work inside of -your fork. Upon completion, commit your finished project back to your fork, and -make a pull request to the master repository. You should include a README.md -file in the root directory detailing the following - -* A brief description of the project and specific features you implemented -* At least one screenshot of your project running. -* A link to a video of your project running. -* Instructions for building and running your project if they differ from the - base code. -* A performance writeup as detailed above. -* A list of all third-party code used. -* This Readme file edited as described above in the README section. +I used stats.min.js to keep track of the time used in rendering every frame. I recorded the time per frame with different fragment shaders on the globe: basic texture, basic texture + specular map, basic texture + specular map + bump map, basic texture + specular map + bump map + cloud, basic texture + specular map + bump map + water. The time per frame is around 17ms for all these shaders, since requestAnimationFrame runs at a maximum of 60 times per second and the shaders are not complicated enough to slow things done. \ No newline at end of file diff --git a/part1/vert_wave.html b/part1/vert_wave.html index 57107ca..1e5bb48 100644 --- a/part1/vert_wave.html +++ b/part1/vert_wave.html @@ -14,20 +14,26 @@ attribute vec2 position; uniform mat4 u_modelViewPerspective; + uniform float u_time; + + varying float height; void main(void) { - float height = 0.0; + float s_contrib = sin(position.x*2.0*3.14159 + u_time); + float t_contrib = cos(position.y*2.0*3.14159 + u_time); + height = s_contrib*t_contrib; gl_Position = u_modelViewPerspective * vec4(vec3(position, height), 1.0); } diff --git a/part1/vert_wave.js b/part1/vert_wave.js index b90b9cf..9666520 100644 --- a/part1/vert_wave.js +++ b/part1/vert_wave.js @@ -28,9 +28,12 @@ var view = mat4.create(); mat4.lookAt(eye, center, up, view); + var time = 0; + var positionLocation = 0; var heightLocation = 1; var u_modelViewPerspectiveLocation; + var u_timeLocation; (function initializeShader() { var program; @@ -40,6 +43,7 @@ var program = createProgram(context, vs, fs, message); context.bindAttribLocation(program, positionLocation, "position"); u_modelViewPerspectiveLocation = context.getUniformLocation(program,"u_modelViewPerspective"); + u_timeLocation = context.getUniformLocation(program,"u_time"); context.useProgram(program); })(); @@ -138,11 +142,14 @@ var mvp = mat4.create(); mat4.multiply(persp, mv, mvp); + time += 0.02; + /////////////////////////////////////////////////////////////////////////// // Render context.clear(context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT); context.uniformMatrix4fv(u_modelViewPerspectiveLocation, false, mvp); + context.uniform1f(u_timeLocation, time); context.drawElements(context.LINES, numberOfIndices, context.UNSIGNED_SHORT,0); window.requestAnimFrame(animate); diff --git a/part1/vert_wave_custom.html b/part1/vert_wave_custom.html new file mode 100644 index 0000000..dcb9715 --- /dev/null +++ b/part1/vert_wave_custom.html @@ -0,0 +1,84 @@ + + + +Vertex Wave Simplex + + + + + +
+ + + + + + + + + + + + diff --git a/part1/vert_wave_perlin.html b/part1/vert_wave_perlin.html new file mode 100644 index 0000000..d22d4d5 --- /dev/null +++ b/part1/vert_wave_perlin.html @@ -0,0 +1,89 @@ + + + +Vertex Wave Simplex + + + + + +
+ + + + + + + + + + + + diff --git a/part1/vert_wave_random.html b/part1/vert_wave_random.html new file mode 100644 index 0000000..75b6090 --- /dev/null +++ b/part1/vert_wave_random.html @@ -0,0 +1,85 @@ + + + +Vertex Wave Simplex + + + + + +
+ + + + + + + + + + + + diff --git a/part1/vert_wave_simplex.html b/part1/vert_wave_simplex.html new file mode 100644 index 0000000..7a05546 --- /dev/null +++ b/part1/vert_wave_simplex.html @@ -0,0 +1,86 @@ + + + +Vertex Wave Simplex + + + + + +
+ + + + + + + + + + + + diff --git a/part2/frag_globe.html b/part2/frag_globe.html index 6aa5609..897107d 100644 --- a/part2/frag_globe.html +++ b/part2/frag_globe.html @@ -73,8 +73,18 @@ void main(void) { - // surface normal - normalized after rasterization - vec3 normal = normalize(v_Normal); + float center = texture2D(u_Bump, v_Texcoord).r; + vec2 rightTexcoord = v_Texcoord + vec2(1.0/1024.0, 0.0); + float right = texture2D(u_Bump, rightTexcoord).r; + vec2 topTexcoord = v_Texcoord + vec2(0.0, 1.0/512.0); + float top = texture2D(u_Bump, topTexcoord).r; + vec3 normal = normalize(vec3(center - right, center - top, 0.2)); + + vec3 normalEC = normalize(v_Normal); + mat3 transform = eastNorthUpToEyeCoordinates(v_positionMC, normalEC); + + normal = normalize(transform * normal); + // normalized eye-to-position vector in camera coordinates vec3 eyeToPosition = normalize(v_Position); @@ -84,14 +94,32 @@ float specular = max(dot(toReflectedLight, -eyeToPosition), 0.0); specular = pow(specular, 20.0); - float gammaCorrect = 1.0/1.2; //gamma correct by 1/1.2 + float gammaCorrect = 0.9; //gamma correct by 1/1.2 vec3 dayColor = texture2D(u_DayDiffuse, v_Texcoord).rgb; vec3 nightColor = texture2D(u_Night, v_Texcoord).rgb; + float isOcean = texture2D(u_EarthSpec, v_Texcoord).r; //apply gamma correction to nighttime texture nightColor = pow(nightColor,vec3(gammaCorrect)); - vec3 color = ((0.6 * diffuse) + (0.4 * specular)) * dayColor; + float kd = isOcean == 1.0 ? 0.6 : 1.0; + float ks = 1.0 - kd; + dayColor = ((kd * diffuse) + (ks * specular)) * dayColor; + + vec2 cloudTexcoord = v_Texcoord + vec2(u_time, 0.0); + float cloudTrans = texture2D(u_CloudTrans, cloudTexcoord).r; + vec3 cloudColor = texture2D(u_Cloud, cloudTexcoord).rgb; + dayColor = mix(cloudColor, dayColor, cloudTrans); + nightColor = mix(vec3(0.0), nightColor, cloudTrans); + + vec3 color = mix(nightColor, dayColor, diffuse); + + //rim + float rim = dot(v_Normal, v_Position) + 1.0; + if (rim > 0.0) { + color += vec3(rim/4.0, rim/2.0, rim/2.0); + } + gl_FragColor = vec4(color, 1.0); } @@ -113,6 +141,7 @@ + diff --git a/part2/frag_globe.js b/part2/frag_globe.js index 1d8a877..d821266 100644 --- a/part2/frag_globe.js +++ b/part2/frag_globe.js @@ -3,6 +3,12 @@ /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ /*global getShaderSource,createWebGLContext,createProgram*/ + var stats = new Stats(); + stats.setMode(1); + stats.domElement.style.position = 'absolute'; + stats.domElement.style.top = '10px'; + document.body.appendChild( stats.domElement ); + function sphericalToCartesian( r, a, e ) { var x = r * Math.cos(e) * Math.cos(a); var y = r * Math.sin(e); @@ -31,7 +37,7 @@ mat4.perspective(45.0, canvas.width/canvas.height, 0.1, 100.0, persp); var radius = 5.0; - var azimuth = Math.PI; + var azimuth = Math.PI/2; var elevation = 0.0001; var eye = sphericalToCartesian(radius, azimuth, elevation); @@ -237,7 +243,6 @@ function animate() { /////////////////////////////////////////////////////////////////////////// // Update - var model = mat4.create(); mat4.identity(model); mat4.rotate(model, 23.4/180*Math.PI, [0.0, 0.0, 1.0]); @@ -286,10 +291,14 @@ gl.activeTexture(gl.TEXTURE5); gl.bindTexture(gl.TEXTURE_2D, specTex); gl.uniform1i(u_EarthSpecLocation, 5); + + gl.uniform1f(u_timeLocation, time); + gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT,0); time += 0.001; window.requestAnimFrame(animate); + stats.update(); } var textureCount = 0; diff --git a/part2/frag_globe_perlin.html b/part2/frag_globe_perlin.html new file mode 100644 index 0000000..0b421c9 --- /dev/null +++ b/part2/frag_globe_perlin.html @@ -0,0 +1,217 @@ + + + +Fragment Globe + + + + + +
+ + + + + + + + + + + + + diff --git a/part2/frag_globe_perlin_vert.html b/part2/frag_globe_perlin_vert.html new file mode 100644 index 0000000..7111451 --- /dev/null +++ b/part2/frag_globe_perlin_vert.html @@ -0,0 +1,233 @@ + + + +Fragment Globe + + + + + +
+ + + + + + + + + + + + + diff --git a/part2/frag_globe_perlin_vert.js b/part2/frag_globe_perlin_vert.js new file mode 100644 index 0000000..c170b66 --- /dev/null +++ b/part2/frag_globe_perlin_vert.js @@ -0,0 +1,332 @@ +(function() { + "use strict"; + /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ + /*global getShaderSource,createWebGLContext,createProgram*/ + + var stats = new Stats(); + stats.setMode(1); + stats.domElement.style.position = 'absolute'; + stats.domElement.style.top = '10px'; + document.body.appendChild( stats.domElement ); + + function sphericalToCartesian( r, a, e ) { + var x = r * Math.cos(e) * Math.cos(a); + var y = r * Math.sin(e); + var z = r * Math.cos(e) * Math.sin(a); + + return [x,y,z]; + } + + var NUM_WIDTH_PTS = 64; + var NUM_HEIGHT_PTS = 64; + + var message = document.getElementById("message"); + var canvas = document.getElementById("canvas"); + var gl = createWebGLContext(canvas, message); + if (!gl) { + return; + } + + /////////////////////////////////////////////////////////////////////////// + + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.enable(gl.DEPTH_TEST); + + var persp = mat4.create(); + mat4.perspective(45.0, canvas.width/canvas.height, 0.1, 100.0, persp); + + var radius = 5.0; + var azimuth = Math.PI/2; + var elevation = 0.0001; + + var eye = sphericalToCartesian(radius, azimuth, elevation); + var center = [0.0, 0.0, 0.0]; + var up = [0.0, 1.0, 0.0]; + var view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + var positionLocation; + var normalLocation; + var texCoordLocation; + var u_InvTransLocation; + var u_ModelLocation; + var u_ViewLocation; + var u_PerspLocation; + var u_CameraSpaceDirLightLocation; + var u_DayDiffuseLocation; + var u_NightLocation; + var u_CloudLocation; + var u_CloudTransLocation; + var u_EarthSpecLocation; + var u_EarthSpecVertLocation; + var u_BumpLocation; + var u_timeLocation; + var u_timeVertLocation; + + (function initializeShader() { + var vs = getShaderSource(document.getElementById("vs")); + var fs = getShaderSource(document.getElementById("fs")); + + var program = createProgram(gl, vs, fs, message); + positionLocation = gl.getAttribLocation(program, "Position"); + normalLocation = gl.getAttribLocation(program, "Normal"); + texCoordLocation = gl.getAttribLocation(program, "Texcoord"); + u_ModelLocation = gl.getUniformLocation(program,"u_Model"); + u_ViewLocation = gl.getUniformLocation(program,"u_View"); + u_PerspLocation = gl.getUniformLocation(program,"u_Persp"); + u_InvTransLocation = gl.getUniformLocation(program,"u_InvTrans"); + u_DayDiffuseLocation = gl.getUniformLocation(program,"u_DayDiffuse"); + u_NightLocation = gl.getUniformLocation(program,"u_Night"); + u_CloudLocation = gl.getUniformLocation(program,"u_Cloud"); + u_CloudTransLocation = gl.getUniformLocation(program,"u_CloudTrans"); + u_EarthSpecLocation = gl.getUniformLocation(program,"u_EarthSpec"); + u_EarthSpecVertLocation = gl.getUniformLocation(program,"u_EarthSpecVert"); + u_BumpLocation = gl.getUniformLocation(program,"u_Bump"); + u_timeLocation = gl.getUniformLocation(program,"u_time"); + u_timeVertLocation = gl.getUniformLocation(program,"u_timeVert"); + u_CameraSpaceDirLightLocation = gl.getUniformLocation(program,"u_CameraSpaceDirLight"); + + gl.useProgram(program); + })(); + + var dayTex = gl.createTexture(); + var bumpTex = gl.createTexture(); + var cloudTex = gl.createTexture(); + var transTex = gl.createTexture(); + var lightTex = gl.createTexture(); + var specTex = gl.createTexture(); + + function initLoadedTexture(texture){ + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + gl.bindTexture(gl.TEXTURE_2D, null); + } + + var numberOfIndices; + + (function initializeSphere() { + function uploadMesh(positions, texCoords, indices) { + // Positions + var positionsName = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionsName); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(positionLocation); + + // Normals + var normalsName = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, normalsName); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(normalLocation); + + // TextureCoords + var texCoordsName = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordsName); + gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(texCoordLocation); + + // Indices + var indicesName = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesName); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + } + + var WIDTH_DIVISIONS = NUM_WIDTH_PTS - 1; + var HEIGHT_DIVISIONS = NUM_HEIGHT_PTS - 1; + + var numberOfPositions = NUM_WIDTH_PTS * NUM_HEIGHT_PTS; + + var positions = new Float32Array(3 * numberOfPositions); + var texCoords = new Float32Array(2 * numberOfPositions); + var indices = new Uint16Array(6 * (WIDTH_DIVISIONS * HEIGHT_DIVISIONS)); + + var positionsIndex = 0; + var texCoordsIndex = 0; + var indicesIndex = 0; + var length; + + for( var j = 0; j < NUM_HEIGHT_PTS; ++j ) + { + var inclination = Math.PI * (j / HEIGHT_DIVISIONS); + for( var i = 0; i < NUM_WIDTH_PTS; ++i ) + { + var azimuth = 2 * Math.PI * (i / WIDTH_DIVISIONS); + positions[positionsIndex++] = Math.sin(inclination)*Math.cos(azimuth); + positions[positionsIndex++] = Math.cos(inclination); + positions[positionsIndex++] = Math.sin(inclination)*Math.sin(azimuth); + texCoords[texCoordsIndex++] = i / WIDTH_DIVISIONS; + texCoords[texCoordsIndex++] = j / HEIGHT_DIVISIONS; + } + } + + for( var j = 0; j < HEIGHT_DIVISIONS; ++j ) + { + var index = j*NUM_WIDTH_PTS; + for( var i = 0; i < WIDTH_DIVISIONS; ++i ) + { + indices[indicesIndex++] = index + i; + indices[indicesIndex++] = index + i+1; + indices[indicesIndex++] = index + i+NUM_WIDTH_PTS; + indices[indicesIndex++] = index + i+NUM_WIDTH_PTS; + indices[indicesIndex++] = index + i+1; + indices[indicesIndex++] = index + i+NUM_WIDTH_PTS+1; + } + } + + uploadMesh(positions, texCoords, indices); + numberOfIndices = indicesIndex; + })(); + + var time = 0; + var mouseLeftDown = false; + var mouseRightDown = false; + var lastMouseX = null; + var lastMouseY = null; + + function handleMouseDown(event) { + if( event.button == 2 ) { + mouseLeftDown = false; + mouseRightDown = true; + } + else { + mouseLeftDown = true; + mouseRightDown = false; + } + lastMouseX = event.clientX; + lastMouseY = event.clientY; + } + + function handleMouseUp(event) { + mouseLeftDown = false; + mouseRightDown = false; + } + + function handleMouseMove(event) { + if (!(mouseLeftDown || mouseRightDown)) { + return; + } + var newX = event.clientX; + var newY = event.clientY; + + var deltaX = newX - lastMouseX; + var deltaY = newY - lastMouseY; + + if( mouseLeftDown ) + { + azimuth += 0.01 * deltaX; + elevation += 0.01 * deltaY; + elevation = Math.min(Math.max(elevation, -Math.PI/2+0.001), Math.PI/2-0.001); + } + else + { + radius += 0.01 * deltaY; + radius = Math.min(Math.max(radius, 2.0), 10.0); + } + eye = sphericalToCartesian(radius, azimuth, elevation); + view = mat4.create(); + mat4.lookAt(eye, center, up, view); + + lastMouseX = newX; + lastMouseY = newY; + } + + canvas.onmousedown = handleMouseDown; + canvas.oncontextmenu = function(ev) {return false;}; + document.onmouseup = handleMouseUp; + document.onmousemove = handleMouseMove; + + + function animate() { + /////////////////////////////////////////////////////////////////////////// + // Update + + var model = mat4.create(); + mat4.identity(model); + mat4.rotate(model, 23.4/180*Math.PI, [0.0, 0.0, 1.0]); + mat4.rotate(model, Math.PI, [1.0, 0.0, 0.0]); + //mat4.rotate(model, -time, [0.0, 1.0, 0.0]); + var mv = mat4.create(); + mat4.multiply(view, model, mv); + + var invTrans = mat4.create(); + mat4.inverse(mv, invTrans); + mat4.transpose(invTrans); + + var lightdir = vec3.create([1.0, 0.0, 1.0]); + var lightdest = vec4.create(); + vec3.normalize(lightdir); + mat4.multiplyVec4(view, [lightdir[0], lightdir[1], lightdir[2], 0.0], lightdest); + lightdir = vec3.createFrom(lightdest[0],lightdest[1],lightdest[2]); + vec3.normalize(lightdir); + + /////////////////////////////////////////////////////////////////////////// + // Render + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + gl.uniformMatrix4fv(u_ModelLocation, false, model); + gl.uniformMatrix4fv(u_ViewLocation, false, view); + gl.uniformMatrix4fv(u_PerspLocation, false, persp); + gl.uniformMatrix4fv(u_InvTransLocation, false, invTrans); + + gl.uniform3fv(u_CameraSpaceDirLightLocation, lightdir); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, dayTex); + gl.uniform1i(u_DayDiffuseLocation, 0); + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, bumpTex); + gl.uniform1i(u_BumpLocation, 1); + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, cloudTex); + gl.uniform1i(u_CloudLocation, 2); + gl.activeTexture(gl.TEXTURE3); + gl.bindTexture(gl.TEXTURE_2D, transTex); + gl.uniform1i(u_CloudTransLocation, 3); + gl.activeTexture(gl.TEXTURE4); + gl.bindTexture(gl.TEXTURE_2D, lightTex); + gl.uniform1i(u_NightLocation, 4); + gl.activeTexture(gl.TEXTURE5); + gl.bindTexture(gl.TEXTURE_2D, specTex); + gl.uniform1i(u_EarthSpecLocation, 5); + gl.uniform1i(u_EarthSpecVertLocation, 5); + + gl.uniform1f(u_timeLocation, time); + gl.uniform1f(u_timeVertLocation, time); + + gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT,0); + + time += 0.001; + window.requestAnimFrame(animate); + stats.update(); + } + + var textureCount = 0; + + function initializeTexture(texture, src) { + texture.image = new Image(); + texture.image.onload = function() { + initLoadedTexture(texture); + + // Animate once textures load. + if (++textureCount === 6) { + animate(); + } + } + texture.image.src = src; + } + + initializeTexture(dayTex, "earthmap1024.png"); + initializeTexture(bumpTex, "earthbump1024.png"); + initializeTexture(cloudTex, "earthcloud1024.png"); + initializeTexture(transTex, "earthtrans1024.png"); + initializeTexture(lightTex, "earthlight1024.png"); + initializeTexture(specTex, "earthspec1024.png"); +}()); diff --git a/part2/stats.min.js b/part2/stats.min.js new file mode 100644 index 0000000..8195d09 --- /dev/null +++ b/part2/stats.min.js @@ -0,0 +1,5 @@ +Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; + i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); + k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= + "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= + a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; diff --git a/screenshots/globe.PNG b/screenshots/globe.PNG new file mode 100644 index 0000000..e81bb38 Binary files /dev/null and b/screenshots/globe.PNG differ diff --git a/screenshots/perlinwave.PNG b/screenshots/perlinwave.PNG new file mode 100644 index 0000000..ab80674 Binary files /dev/null and b/screenshots/perlinwave.PNG differ diff --git a/screenshots/simplexwave1.PNG b/screenshots/simplexwave1.PNG new file mode 100644 index 0000000..f296830 Binary files /dev/null and b/screenshots/simplexwave1.PNG differ diff --git a/screenshots/simplexwave2.PNG b/screenshots/simplexwave2.PNG new file mode 100644 index 0000000..24c20a9 Binary files /dev/null and b/screenshots/simplexwave2.PNG differ diff --git a/screenshots/sinewave.PNG b/screenshots/sinewave.PNG new file mode 100644 index 0000000..73c72d7 Binary files /dev/null and b/screenshots/sinewave.PNG differ diff --git a/screenshots/water1.PNG b/screenshots/water1.PNG new file mode 100644 index 0000000..367d5e3 Binary files /dev/null and b/screenshots/water1.PNG differ diff --git a/screenshots/water2.PNG b/screenshots/water2.PNG new file mode 100644 index 0000000..177c639 Binary files /dev/null and b/screenshots/water2.PNG differ