diff --git a/Links.txt b/Links.txt new file mode 100644 index 0000000..909f324 --- /dev/null +++ b/Links.txt @@ -0,0 +1,9 @@ +http://msdn.microsoft.com/en-us/library/ie/dn302449(v=vs.85).aspx +http://workshop.chromeexperiments.com/examples/gui/#4--Color-Controllers +http://jsfiddle.net/mortennobel/URvtx/ +http://bl.ocks.org/g-k/5847236 +http://jayconrod.com/posts/34/water-simulation-in-glsl +https://glsleffects.codeplex.com/wikipage?title=Ripple +http://www.dreamincode.net/forums/topic/66480-perlin-noise/ +https://github.com/mrdoob/stats.js +http://gamedev.stackexchange.com/questions/60313/implementing-a-skybox-with-glsl-version-330 \ No newline at end of file diff --git a/Pics/All Effects.bmp b/Pics/All Effects.bmp new file mode 100644 index 0000000..b769eec Binary files /dev/null and b/Pics/All Effects.bmp differ diff --git a/Pics/HeightMap Shader.bmp b/Pics/HeightMap Shader.bmp new file mode 100644 index 0000000..4344b7b Binary files /dev/null and b/Pics/HeightMap Shader.bmp differ diff --git a/Pics/Ripple.bmp b/Pics/Ripple.bmp new file mode 100644 index 0000000..a84d395 Binary files /dev/null and b/Pics/Ripple.bmp differ diff --git a/Pics/Vertex Wave.bmp b/Pics/Vertex Wave.bmp new file mode 100644 index 0000000..ce6f5e7 Binary files /dev/null and b/Pics/Vertex Wave.bmp differ diff --git a/Pics/Water.bmp b/Pics/Water.bmp new file mode 100644 index 0000000..3d84df7 Binary files /dev/null and b/Pics/Water.bmp differ diff --git a/README.md b/README.md index 2d26873..701b3f9 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,37 @@ -------------------------------------------------------------------------------- +======================================= CIS565: Project 5: WebGL -------------------------------------------------------------------------------- -Fall 2014 -------------------------------------------------------------------------------- -Due Monday 11/03/2014 -------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- -NOTE: -------------------------------------------------------------------------------- -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. - -------------------------------------------------------------------------------- -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. +====================================== +Fall 2014
+Bo Zhang
-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. +![Alt text](https://github.com/wulinjiansheng/Project5-WebGL/blob/master/Pics/All%20Effects.bmp) -------------------------------------------------------------------------------- -CONTENTS: -------------------------------------------------------------------------------- -The Project5 root directory contains the following subdirectories: - -* js/ contains the javascript files, including external libraries, necessary. -* assets/ contains the textures that will be used in the second half of the - assignment. -* resources/ contains the screenshots found in this readme file. - -------------------------------------------------------------------------------- -PART 1 REQUIREMENTS: -------------------------------------------------------------------------------- - -In Part 1, you are given code for: +##Overview -* 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) - -* One interesting vertex shader of your choice +This is a WebGL project based on implementation of GLSL vertex shader and GLSL fragment shader. ------------------------------------------------------------------------------- -PART 1 WALKTHROUGH: +PART 1 : ------------------------------------------------------------------------------- -**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) +* Sin-wave based vertex shader: +![Alt text](https://github.com/wulinjiansheng/Project5-WebGL/blob/master/Pics/Vertex%20Wave.bmp) +* Ripple Shader: +![Alt text](https://github.com/wulinjiansheng/Project5-WebGL/blob/master/Pics/Ripple.bmp) -* 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`. - - * Using dat.gui, you will add color pickers to modify the max and min colors - via GUI. You will do this by adding the proper uniforms to the fragment - shader, and using the addColor function from dat.GUI. - -* 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()`. - -**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. +About ripple: +I implement the ripple shader based on this link: https://glsleffects.codeplex.com/wikipage?title=Ripple, the main idea is as fllowing: +``` + float dist = distance(vec2(position.x,position.y),vec2(CenterPointX,CenterPointY)); + float height = Amplitude*sin(dist/(Wavelength/10.0) + u_time)*pow(Attenuation,dist); +``` +* "CenterPoint" is the source of ripple, all circles of a ripple should has the same center point. +* "Amplitude" the amplitude defines how big the was is. The range of sin(x) is [-1,1], so, the height of ripple should be amplitude*sin(x). The x is the distance between current point and the center point, will be discussed next. +* "Attenuation", the power of a wave will weak down as it travels, the attenuation factor defines how fast the wave will weak down. +* "Wavelength" is how far the adjacent two points of the same height is. +PART 2: ------------------------------------------------------------------------------- -PART 2 REQUIREMENTS: -------------------------------------------------------------------------------- -In Part 2, you are given code for: - +Given Features: * Reading and loading textures * Rendering a sphere with textures mapped on * Basic passthrough fragment and vertex shaders @@ -127,236 +41,45 @@ In Part 2, you are given code for: * left-click and drag moves the camera around * right-click and drag moves the camera in and out -You are required to implement: - +Basic Features: * 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: - +Extra Features: * 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 -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. +#### Procedural water rendering and animation using noise +To implement this feature, I make a perlin noise generator based on this link(http://www.dreamincode.net/forums/topic/66480-perlin-noise/): +The only different thing is that we can't use bitwise operators in glsl, so I use a rand generator for the findnoise(). I adjust the parameters and here is the final effect (See the waves on ocean):
+![Alt text](https://github.com/wulinjiansheng/Project5-WebGL/blob/master/Pics/Water.bmp) +
+
+#### Shade based on altitude using the height map +To implement this feature, I mix the user-chosen colors with the original color on land according to the altitude of the land. And here is the result with heightmap based shader:
+![Alt text](https://github.com/wulinjiansheng/Project5-WebGL/blob/master/Pics/HeightMap%20Shader.bmp) -Some examples of what your completed globe renderer will look like: - -![Completed globe, day side](resources/globe_day.png) - -Figure 0. Completed globe renderer, daylight side. - -![Completed globe, twilight](resources/globe_twilight.png) - -Figure 1. Completed globe renderer, twilight border. - -![Completed globe, night side](resources/globe_night.png) - -Figure 2. Completed globe renderer, night side. +Performance Evaluation ------------------------------------------------------------------------------- -PART 2 WALKTHROUGH: -------------------------------------------------------------------------------- - -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** +Currently, the frame rate counter always shows 60 FPS no matter I enable all the extra effects or not. -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** - -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. - -For more information on rim lighting, -read http://www.fundza.com/rman_shaders/surface/rim_effects/index.html. - -------------------------------------------------------------------------------- -GH-PAGES +Third Party Code ------------------------------------------------------------------------------- -Since this assignment is in WebGL you will make your project easily viewable by -taking advantage of GitHub's project pages feature. - -Once you are done you will need to create a new branch named gh-pages: - -`git branch gh-pages` - -Switch to your new branch: - -`git checkout gh-pages` - -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 - -`.github.io/` - -to see your beautiful globe from anywhere. +* JavaScript Performance Monitor(https://github.com/mrdoob/stats.js)
+I use this to do the Performance Evaluation. +Personal Link ------------------------------------------------------------------------------- -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). +http://youtu.be/tTX-V_cDxzY
+Web Page ------------------------------------------------------------------------------- -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. - -In this homework, we do not expect crazy performance evaluation in terms of -optimizations. However, it would be good to take performance benchmarks at -every step in this assignment to see how complicated fragment shaders affect the -overall speed. You can do this by using stats.js. +http://wulinjiansheng.github.io/Project5-WebGL/ -------------------------------------------------------------------------------- -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 Harmony, - harmoli+cis565@seas.upenn.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. diff --git a/frag_globe.html b/frag_globe.html index e074492..42f9b58 100644 --- a/frag_globe.html +++ b/frag_globe.html @@ -1,119 +1,263 @@ - -Fragment Globe - - - - - -
- - - - - - - - + normal = eastNorthUpToEyeCoordinates(v_positionMC,v_Normal) * normal; + normal = normalize(normal); + + // normalized eye-to-position vector in camera coordinates + vec3 eyeToPosition = normalize(v_Position); + + float diffuse = clamp(dot(u_CameraSpaceDirLight, normal), 0.0, 1.0); + + vec3 toReflectedLight = reflect(-u_CameraSpaceDirLight, normal); + 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 + + vec3 dayColor = texture2D(u_DayDiffuse, v_Texcoord).rgb; + vec3 nightColor = texture2D(u_Night, v_Texcoord).rgb; + //apply gamma correction to nighttime texture + nightColor = pow(nightColor,vec3(gammaCorrect)); + + if(EarthSpec<0.5) + { + specpart = 0.0; + specular = 0.0; + diffpart = 1.0; + } + + vec3 color; + + float altitude = texture2D(u_Bump, v_Texcoord).r; + vec3 d_color = ((diffpart * diffuse) + (specpart * specular)) * dayColor; + vec3 n_color = nightColor; + if(EarthSpec<0.1 && u_heightmap==1) + { + vec3 hcolor; + if(altitude>=0.0 && altitude <= 0.3) + hcolor = u_hcolor1/255.0; + else if(altitude>0.3 && altitude <= 0.6) + { + float mixa = (altitude - 0.3) / 0.3; + hcolor = mix(u_hcolor1/255.0,u_hcolor2/255.0,mixa); + } + else if(altitude>0.6) + { + float mixa = (altitude - 0.6) / 0.4; + hcolor = mix(u_hcolor2/255.0,u_hcolor3/255.0,mixa); + } + + d_color = mix(d_color,hcolor,altitude); + } + + + + //Clouds + vec3 cloudsColor = texture2D(u_Cloud,vec2(v_Texcoord.s - 0.05 * u_time,v_Texcoord.t)).rgb; + float cloudMix = texture2D(u_CloudTrans,vec2(v_Texcoord.s - 0.05 * u_time,v_Texcoord.t)).r; + d_color = mix(cloudsColor,d_color,cloudMix); + n_color = 1.5 * mix(vec3(0.0,0.0,0.0),n_color,cloudMix); + + //Night Lights + float rperiod = 0.1; //transition from day to night + float diffjudge = dot(u_CameraSpaceDirLight, normal); + if(diffjudge >= rperiod) + color = d_color; + else if(diffjudge <= -rperiod) + color = n_color; + else + { + float alpha = (diffjudge + rperiod)/(2.0*rperiod); + color = mix(n_color,d_color,alpha); + } + + //Rim Lighting + float rim = dot(v_Normal,v_Position) + 1.0; + vec3 rimcolor = vec3(0.0,0.0,0.0); + if(rim>0.0) + rimcolor = vec3(rim/4.0, rim/2.0, rim/2.0); + + rimcolor = clamp(rimcolor, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0)); + gl_FragColor = vec4(color + rimcolor, 1.0); +} + +mat3 eastNorthUpToEyeCoordinates(vec3 positionMC, vec3 normalEC) +{ + // normalized surface tangent in model coordinates + vec3 tangentMC = normalize(vec3(-positionMC.z, positionMC.x, 0.0)); + // normalized surface tangent in eye coordiantes + vec3 tangentEC = normalize(mat3(u_InvTrans) * tangentMC); + // normalized surface bitangent in eye coordinates + vec3 bitangentEC = normalize(cross(normalEC, tangentEC)); + + return mat3( + tangentEC.x, tangentEC.y, tangentEC.z, + bitangentEC.x, bitangentEC.y, bitangentEC.z, + normalEC.x, normalEC.y, normalEC.z); +} + + + + + + + + - + diff --git a/js/frag_globe.js b/js/frag_globe.js index f37830d..927085b 100644 --- a/js/frag_globe.js +++ b/js/frag_globe.js @@ -1,14 +1,14 @@ -(function() { +(function () { "use strict"; /*global window,document,Float32Array,Uint16Array,mat4,vec3,snoise*/ /*global getShaderSource,createWebGLContext,createProgram*/ - function sphericalToCartesian( r, a, e ) { + 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]; + return [x, y, z]; } var NUM_WIDTH_PTS = 64; @@ -28,7 +28,7 @@ gl.enable(gl.DEPTH_TEST); var persp = mat4.create(); - mat4.perspective(45.0, canvas.width/canvas.height, 0.1, 100.0, persp); + mat4.perspective(45.0, canvas.width / canvas.height, 0.1, 100.0, persp); var radius = 5.0; var azimuth = Math.PI; @@ -56,6 +56,14 @@ var u_BumpLocation; var u_timeLocation; + //Added + var stats; + var u_heightmapLocation; + var u_hcolor1Location; + var u_hcolor2Location; + var u_hcolor3Location; + var u_waternoiseLocation; + (function initializeShader() { var vs = getShaderSource(document.getElementById("vs")); var fs = getShaderSource(document.getElementById("fs")); @@ -64,30 +72,37 @@ 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_BumpLocation = gl.getUniformLocation(program,"u_Bump"); - u_timeLocation = gl.getUniformLocation(program,"u_time"); - u_CameraSpaceDirLightLocation = gl.getUniformLocation(program,"u_CameraSpaceDirLight"); + 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_BumpLocation = gl.getUniformLocation(program, "u_Bump"); + u_timeLocation = gl.getUniformLocation(program, "u_time"); + u_CameraSpaceDirLightLocation = gl.getUniformLocation(program, "u_CameraSpaceDirLight"); + + //Added + u_heightmapLocation = gl.getUniformLocation(program, "u_heightmap"); + u_hcolor1Location = gl.getUniformLocation(program, "u_hcolor1"); + u_hcolor2Location = gl.getUniformLocation(program, "u_hcolor2"); + u_hcolor3Location = gl.getUniformLocation(program, "u_hcolor3"); + u_waternoiseLocation = gl.getUniformLocation(program, "u_waternoise"); gl.useProgram(program); })(); - var dayTex = gl.createTexture(); - var bumpTex = gl.createTexture(); + var dayTex = gl.createTexture(); + var bumpTex = gl.createTexture(); var cloudTex = gl.createTexture(); var transTex = gl.createTexture(); var lightTex = gl.createTexture(); - var specTex = gl.createTexture(); + var specTex = gl.createTexture(); - function initLoadedTexture(texture){ + 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); @@ -108,14 +123,14 @@ 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); @@ -143,31 +158,27 @@ var indicesIndex = 0; var length; - for( var j = 0; j < NUM_HEIGHT_PTS; ++j ) - { + 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 ) - { + 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.sin(inclination) * Math.cos(azimuth); positions[positionsIndex++] = Math.cos(inclination); - positions[positionsIndex++] = Math.sin(inclination)*Math.sin(azimuth); + 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; + 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; } } @@ -182,7 +193,7 @@ var lastMouseY = null; function handleMouseDown(event) { - if( event.button == 2 ) { + if (event.button == 2) { mouseLeftDown = false; mouseRightDown = true; } @@ -208,15 +219,13 @@ var deltaX = newX - lastMouseX; var deltaY = newY - lastMouseY; - - if( mouseLeftDown ) - { + + 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); + elevation = Math.min(Math.max(elevation, -Math.PI / 2 + 0.001), Math.PI / 2 - 0.001); } - else - { + else { radius += 0.01 * deltaY; radius = Math.min(Math.max(radius, 2.0), 10.0); } @@ -229,18 +238,57 @@ } canvas.onmousedown = handleMouseDown; - canvas.oncontextmenu = function(ev) {return false;}; + canvas.oncontextmenu = function (ev) { return false; }; document.onmouseup = handleMouseUp; document.onmousemove = handleMouseMove; + //Added + function initStats() { + stats = new Stats(); + stats.setMode(0); // 0: fps, 1: ms + + // Align top-left + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + + document.body.appendChild(stats.domElement); + + + return stats; + } + + //Added + var Datas = { + Heightmap: true, + heightcolor1: [0, 255, 0], + heightcolor2: [255, 255, 0], + heightcolor3: [255, 0, 0], + Waternoise: true + }; + + // Function called when the window is loaded + window.onload = function () { + // Add GUI component + var gui = new dat.GUI(); + gui.add(Datas, 'Heightmap'); + gui.addColor(Datas, 'heightcolor1'); + gui.addColor(Datas, 'heightcolor2'); + gui.addColor(Datas, 'heightcolor3'); + gui.add(Datas, 'Waternoise'); + stats = initStats(); + }; + function animate() { + if (stats) + stats.update(); /////////////////////////////////////////////////////////////////////////// // 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, 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(); @@ -254,7 +302,7 @@ 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]); + lightdir = vec3.createFrom(lightdest[0], lightdest[1], lightdest[2]); vec3.normalize(lightdir); /////////////////////////////////////////////////////////////////////////// @@ -286,17 +334,32 @@ gl.activeTexture(gl.TEXTURE5); gl.bindTexture(gl.TEXTURE_2D, specTex); gl.uniform1i(u_EarthSpecLocation, 5); - gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT,0); + gl.drawElements(gl.TRIANGLES, numberOfIndices, gl.UNSIGNED_SHORT, 0); + gl.uniform1f(u_timeLocation, time); + + //Added + if(Datas.Heightmap) + gl.uniform1i(u_heightmapLocation, 1); + else + gl.uniform1i(u_heightmapLocation, 0); + gl.uniform3f(u_hcolor1Location, Datas.heightcolor1[0], Datas.heightcolor1[1], Datas.heightcolor1[2]); + gl.uniform3f(u_hcolor2Location, Datas.heightcolor2[0], Datas.heightcolor2[1], Datas.heightcolor2[2]); + gl.uniform3f(u_hcolor3Location, Datas.heightcolor3[0], Datas.heightcolor3[1], Datas.heightcolor3[2]); + + if(Datas.Waternoise) + gl.uniform1i(u_waternoiseLocation, 1); + else + gl.uniform1i(u_waternoiseLocation, 0); time += 0.001; window.requestAnimFrame(animate); } var textureCount = 0; - + function initializeTexture(texture, src) { texture.image = new Image(); - texture.image.onload = function() { + texture.image.onload = function () { initLoadedTexture(texture); // Animate once textures load. @@ -313,4 +376,4 @@ initializeTexture(transTex, "assets/earthtrans1024.png"); initializeTexture(lightTex, "assets/earthlight1024.png"); initializeTexture(specTex, "assets/earthspec1024.png"); -}()); +} ()); diff --git a/js/lib/Stats.js b/js/lib/Stats.js new file mode 100644 index 0000000..90b2a27 --- /dev/null +++ b/js/lib/Stats.js @@ -0,0 +1,149 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +var Stats = function () { + + var startTime = Date.now(), prevTime = startTime; + var ms = 0, msMin = Infinity, msMax = 0; + var fps = 0, fpsMin = Infinity, fpsMax = 0; + var frames = 0, mode = 0; + + var container = document.createElement( 'div' ); + container.id = 'stats'; + container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); + container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; + + var fpsDiv = document.createElement( 'div' ); + fpsDiv.id = 'fps'; + fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; + container.appendChild( fpsDiv ); + + var fpsText = document.createElement( 'div' ); + fpsText.id = 'fpsText'; + fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + fpsText.innerHTML = 'FPS'; + fpsDiv.appendChild( fpsText ); + + var fpsGraph = document.createElement( 'div' ); + fpsGraph.id = 'fpsGraph'; + fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; + fpsDiv.appendChild( fpsGraph ); + + while ( fpsGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; + fpsGraph.appendChild( bar ); + + } + + var msDiv = document.createElement( 'div' ); + msDiv.id = 'ms'; + msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; + container.appendChild( msDiv ); + + var msText = document.createElement( 'div' ); + msText.id = 'msText'; + msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + msText.innerHTML = 'MS'; + msDiv.appendChild( msText ); + + var msGraph = document.createElement( 'div' ); + msGraph.id = 'msGraph'; + msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; + msDiv.appendChild( msGraph ); + + while ( msGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; + msGraph.appendChild( bar ); + + } + + var setMode = function ( value ) { + + mode = value; + + switch ( mode ) { + + case 0: + fpsDiv.style.display = 'block'; + msDiv.style.display = 'none'; + break; + case 1: + fpsDiv.style.display = 'none'; + msDiv.style.display = 'block'; + break; + } + + }; + + var updateGraph = function ( dom, value ) { + + var child = dom.appendChild( dom.firstChild ); + child.style.height = value + 'px'; + + }; + + return { + + REVISION: 12, + + domElement: container, + + setMode: setMode, + + begin: function () { + + startTime = Date.now(); + + }, + + end: function () { + + var time = Date.now(); + + ms = time - startTime; + msMin = Math.min( msMin, ms ); + msMax = Math.max( msMax, ms ); + + msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; + updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); + + frames ++; + + if ( time > prevTime + 1000 ) { + + fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); + fpsMin = Math.min( fpsMin, fps ); + fpsMax = Math.max( fpsMax, fps ); + + fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; + updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); + + prevTime = time; + frames = 0; + + } + + return time; + + }, + + update: function () { + + startTime = this.end(); + + } + + } + +}; + +if ( typeof module === 'object' ) { + + module.exports = Stats; + +} \ No newline at end of file diff --git a/js/lib/stats.min.js b/js/lib/stats.min.js new file mode 100644 index 0000000..52539f4 --- /dev/null +++ b/js/lib/stats.min.js @@ -0,0 +1,6 @@ +// stats.js - http://github.com/mrdoob/stats.js +var 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:12,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()}}};"object"===typeof module&&(module.exports=Stats); diff --git a/vert_wave.html b/vert_wave.html index 5c7495b..b895111 100644 --- a/vert_wave.html +++ b/vert_wave.html @@ -18,11 +18,21 @@ uniform mat4 u_modelViewPerspective; + uniform float u_time; + uniform vec3 u_mincolor; + uniform vec3 u_maxcolor; + + varying vec3 fs_color; //input + void main(void) { // NOTE : according to the WebGL standard, 0.0f is not accepted - 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); + float height = s_contrib * t_contrib; + float alpha = (height+1.0)/2.0; + fs_color = mix(u_mincolor/255.0, u_maxcolor/255.0,alpha ); // NOTE : gl_Position is always a vec4 gl_Position = u_modelViewPerspective * vec4(vec3(position, height), 1.0); } @@ -32,11 +42,11 @@ precision mediump float; uniform vec4 u_color; - + varying vec3 fs_color; //output void main(void) { // NOTE : gl_FragColor is always a vec4 - gl_FragColor = u_color; + gl_FragColor = vec4(fs_color,1.0); } @@ -64,15 +74,27 @@ var persp = mat4.create(); var view = mat4.create(); - - // Function called when the window is loaded - window.onload = function() { - // Add GUI component - var gui = new dat.GUI(); - init(); + //Added + var time = 0.0; + var u_timeLocation; + var u_mincolorLocation; + var u_maxcolorLocation; - animate(); + var Colors = { + mincolor : [0, 204, 255], + maxcolor : [255, 51, 0] + }; + + // Function called when the window is loaded + window.onload = function () { + // Add GUI component + var gui = new dat.GUI(); + + gui.addColor(Colors, 'mincolor'); + gui.addColor(Colors, 'maxcolor'); + init(); + animate(); }; function init() { @@ -94,7 +116,7 @@ mat4.lookAt(eye, center, up, view); initializeShader(); - initializeGrid(); + initializeGrid(); } @@ -108,11 +130,23 @@ var mvp = mat4.create(); mat4.multiply(persp, mv, mvp); + //Added + time = time + 0.02; + if (time == 3.14159 * 100.0) + time = 0.0; + + // Render context.clear(context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT); - context.uniformMatrix4fv(u_modelViewPerspectiveLocation, false, mvp); - context.drawElements(context.LINES, numberOfIndices, context.UNSIGNED_SHORT,0); + context.uniformMatrix4fv(u_modelViewPerspectiveLocation, false, mvp); + context.drawElements(context.LINES, numberOfIndices, context.UNSIGNED_SHORT, 0); + + //Added + context.uniform1f(u_timeLocation, time); + + context.uniform3f(u_mincolorLocation, Colors.mincolor[0], Colors.mincolor[1], Colors.mincolor[2]); + context.uniform3f(u_maxcolorLocation, Colors.maxcolor[0], Colors.maxcolor[1], Colors.maxcolor[2]); window.requestAnimFrame(animate); } @@ -127,6 +161,11 @@ u_modelViewPerspectiveLocation = context.getUniformLocation(program,"u_modelViewPerspective"); u_colorLocation = context.getUniformLocation(program, "u_color"); + //Added + u_timeLocation = context.getUniformLocation(program, "u_time"); + u_mincolorLocation = context.getUniformLocation(program, "u_mincolor"); + u_maxcolorLocation = context.getUniformLocation(program, "u_maxcolor"); + context.useProgram(program); } diff --git a/vert_wave_custom.html b/vert_wave_custom.html new file mode 100644 index 0000000..81eede5 --- /dev/null +++ b/vert_wave_custom.html @@ -0,0 +1,288 @@ + + + + Ripple + + + + + +
+ + + + + + + + + + + + + +