diff --git a/README.md b/README.md
index 919af39..21346b9 100644
--- a/README.md
+++ b/README.md
@@ -3,142 +3,117 @@ CIS565: Project 6: Deferred Shader
-------------------------------------------------------------------------------
Fall 2013
-------------------------------------------------------------------------------
-Due Friday 11/15/2013
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-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.
+---
+Deferred Shading
+---
--------------------------------------------------------------------------------
-INTRODUCTION:
--------------------------------------------------------------------------------
-In this project, you will get introduced to the basics of deferred shading. You will write GLSL and OpenGL code to perform various tasks in a deferred lighting pipeline such as creating and writing to a G-Buffer.
+The whole point of deferred shading is to separate geometry from lighting calculations and change the lighting equation from an O(GE) into an O(G+E) problem. To that effect, I rendered the Sponza scene with a large number of lights.
--------------------------------------------------------------------------------
-CONTENTS:
--------------------------------------------------------------------------------
-The Project6 root directory contains the following subdirectories:
-
-* base/
- * PROJ_WIN/ contains the vs2010 project files
- * PROJ_NIX/ contains makefile for building (tested on ubuntu 12.04 LTS)
- * res/ contains resources including shader source and obj files
- * src/ contains the c++ code for the project along with SOIL and tiny_obj_loader
-* shared32/ contains freeglut, glm, and glew.
+333K Lights, randomly colored and positioned
--------------------------------------------------------------------------------
-REQUIREMENTS:
--------------------------------------------------------------------------------
+
-In this project, you are given code for:
-* Loading .obj files
-* Rendering to a minimal G buffer:
- * Depth
- * Normal
- * Color
- * Eye space position
-* Rendering simple ambient and directional lighting to texture
-* Example post process shader to add a vignette
-
-You are required to implement:
-* Either of the following effects
- * Bloom (feel free to use [GPU Gems](http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html) as a rough guide)
- * "Toon" Shading (with basic silhouetting)
-* Point light sources
-* An additional G buffer slot and some effect showing it off
-
-**NOTE**: Implementing separable convolution will require another link in your pipeline and will count as an extra feature if you do performance analysis with a standard one-pass 2D convolution. The overhead of rendering and reading from a texture _may_ offset the extra computations for smaller 2D kernels.
-
-You must implement two of the following extras:
-* The effect you did not choose above
-* Screen space ambient occlusion
-* Compare performance to a normal forward renderer with
- * No optimizations
- * Coarse sort geometry front-to-back for early-z
- * Z-prepass for early-z
-* Optimize g-buffer format, e.g., pack things together, quantize, reconstruct z from normal x and y (because it is normalized), etc.
- * Must be accompanied with a performance analysis to count
-* Additional lighting and pre/post processing effects! (email first please, if they are good you may add multiple).
+15K Lights
--------------------------------------------------------------------------------
-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).
+###Controls
+* Up-Down : Change Y size of kernel
+* Left-Right : Change X size of kernel
+* [ : Toggle back one step in *modes*
+* ] : Toggle forward one step in *modes*
+* 1 : Depth Visual
+* 2 : Normal Visual
+* 3 : Color Visual
+* 4 : Position Visual
+* 5 : Light Visual
+* 0 : Main Mode
--------------------------------------------------------------------------------
-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.
+###Modes
+* Simple pass through effect
+* Bloom with 2D Kernel
+* Bloom with Separable Kernel
+* Glow with 2D Kernel
+* Bloom with Separable Kernel
+* Vignette
+* SSAO Pass
+* SSAO Blended Pass
-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.
+---
+Features
+---
--------------------------------------------------------------------------------
-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.
+The following features were implemented.
--------------------------------------------------------------------------------
-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.
+####Bloom Shading (2D separable kernels implemented)
+All pixels beyond a certain threshold in value (after lighting) were blurred based on the x and y radius. This can be seen below:
+
+No Bloom:
+
+
+
+Y-direction only bloom:
+
+
+
+The separability of the 2D Gaussian kernel was exploited using yet another frame buffer object and extra textures to ping-pong with and uses the following pipeline:
+
+
+
+This separability of blur is a massive speed improvement. This is discussed further in the performance section.
+
+####Glow Shading : Extra G-Buffer slot
+
+The object "short box" is selected based on its name and made to glow using a similar separable-blur technique as is used for the bloom shading. This uses an extra G-Buffer slot: the alpha channel of the position texture. A value of 0 or 1 is written out here based on whether the object being rendered is the "short box" or not.
+
+No Glow:
+
+
+
+With Glow:
+
+
+
+####Specular Highlights: Compacted G-Buffer usage
+
+Since I'm personally fond of specular highlights, I decided to use them in the code. I've picked up the specular value from the mtl files of the obj loaded and it is packed into a G Buffer during the first phase of the system. Since the glow buffer is quite a waste of a 32 bit floating point value for just the 0 or 1 glow, I decided to use it for specular exponent as well.
+
+The buffer is packed as ````2 * spec_ex + glow? ```` and decoded when required.
+
+You can see the specular as a little white dash on the green wall and a little bit on the blue box as well.
+
+
+
+####Screen Space Ambient Occlusion (attempt)
+I based my implementation on this [paper](http://graphics.cs.williams.edu/papers/SAOHPG12/) and referred to their implementation and pseudo code along with trying to over randomize the inputs to the code. I couldn't get it to work entirely correctly but this is the result I get.
+
+
+
+
+
+####Light Visualization
+Lights can be visualized in the scene as follows
+
+
+
+---
+Performance
+---
+I must say, I'm very impressed with the number of lights that are handled by a deferred shading pipeline for extremely complex geometry without completely blocking up the GPU. It's amazing to see this delineation of lighting calculation and a nice geometry pass.
+
+Since I did separable kernels, I decided to graph out the time. As noticed, since the separation of a 2D kernel results in a change from O(MN) to O(M+N) (quite like deferred shading itself!), we are able to run much much larger kernel sizes off the bat. We notice that there is a slight overhead of creating yet another frame buffer object and having to setup another texture, but compared to the speed up, this cost is marginal. It is overtaken by the lowest of texture look up calls (10 vs 6+overhead).
+
---
-SUBMISSION
+Coming up soon
---
-As with the previous projects, 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 have a few cool screen space effects planned out in the near future.
---
ACKNOWLEDGEMENTS
---
-This project makes use of [tinyobjloader](http://syoyo.github.io/tinyobjloader/) and [SOIL](http://lonesock.net/soil.html)
+* This project makes use of [tinyobjloader](http://syoyo.github.io/tinyobjloader/) and [SOIL](http://lonesock.net/soil.html)
+* For reference on SSAO implementation [Separabe, screen space Ambient Occlusion](http://floored.com/blog/2013/ssao-screen-space-ambient-occlusion.html)
diff --git a/base/PROJ_WIN/P6/P6/P6.vcxproj b/base/PROJ_WIN/P6/P6/P6.vcxproj
index 2d1749c..f6c8293 100644
--- a/base/PROJ_WIN/P6/P6/P6.vcxproj
+++ b/base/PROJ_WIN/P6/P6/P6.vcxproj
@@ -50,7 +50,7 @@
true..\..\..\..\shared32\glew\lib;..\..\..\..\shared32\freeglut\lib;%(AdditionalLibraryDirectories)
- freeglut.lib;glew32.lib;%(AdditionalDependencies)
+ ../Debug/soil.lib;freeglut.lib;glew32.lib;%(AdditionalDependencies)Console
@@ -67,7 +67,7 @@
truetrue..\..\..\..\shared32\glew\lib;..\..\..\..\shared32\freeglut\lib;%(AdditionalLibraryDirectories)
- freeglut.lib;glew32.lib;%(AdditionalDependencies)
+ ../Release/SOIL.lib;freeglut.lib;glew32.lib;%(AdditionalDependencies)Console
diff --git a/base/images/15K_lights.png b/base/images/15K_lights.png
new file mode 100644
index 0000000..47406d8
Binary files /dev/null and b/base/images/15K_lights.png differ
diff --git a/base/images/AOComp.png b/base/images/AOComp.png
new file mode 100644
index 0000000..5e17290
Binary files /dev/null and b/base/images/AOComp.png differ
diff --git a/base/images/AONone.png b/base/images/AONone.png
new file mode 100644
index 0000000..b95364f
Binary files /dev/null and b/base/images/AONone.png differ
diff --git a/base/images/AmbientOcclusion.png b/base/images/AmbientOcclusion.png
new file mode 100644
index 0000000..718842e
Binary files /dev/null and b/base/images/AmbientOcclusion.png differ
diff --git a/base/images/blur_step1.png b/base/images/blur_step1.png
new file mode 100644
index 0000000..a4652ca
Binary files /dev/null and b/base/images/blur_step1.png differ
diff --git a/base/images/blur_step2.png b/base/images/blur_step2.png
new file mode 100644
index 0000000..6561361
Binary files /dev/null and b/base/images/blur_step2.png differ
diff --git a/base/images/blur_step3.png b/base/images/blur_step3.png
new file mode 100644
index 0000000..46a40f3
Binary files /dev/null and b/base/images/blur_step3.png differ
diff --git a/base/images/blur_step4.png b/base/images/blur_step4.png
new file mode 100644
index 0000000..cb80da1
Binary files /dev/null and b/base/images/blur_step4.png differ
diff --git a/base/images/blur_step5.png b/base/images/blur_step5.png
new file mode 100644
index 0000000..d300f83
Binary files /dev/null and b/base/images/blur_step5.png differ
diff --git a/base/images/box_Glow.png b/base/images/box_Glow.png
new file mode 100644
index 0000000..0934626
Binary files /dev/null and b/base/images/box_Glow.png differ
diff --git a/base/images/box_bloom.png b/base/images/box_bloom.png
new file mode 100644
index 0000000..ca681f1
Binary files /dev/null and b/base/images/box_bloom.png differ
diff --git a/base/images/box_noGlow.png b/base/images/box_noGlow.png
new file mode 100644
index 0000000..ce12498
Binary files /dev/null and b/base/images/box_noGlow.png differ
diff --git a/base/images/box_specular.png b/base/images/box_specular.png
new file mode 100644
index 0000000..dd7e8cf
Binary files /dev/null and b/base/images/box_specular.png differ
diff --git a/base/images/chart.png b/base/images/chart.png
new file mode 100644
index 0000000..49ca342
Binary files /dev/null and b/base/images/chart.png differ
diff --git a/base/images/crazy333k.png b/base/images/crazy333k.png
new file mode 100644
index 0000000..312e50f
Binary files /dev/null and b/base/images/crazy333k.png differ
diff --git a/base/images/crazy333k_noAO.png b/base/images/crazy333k_noAO.png
new file mode 100644
index 0000000..0d2d8c3
Binary files /dev/null and b/base/images/crazy333k_noAO.png differ
diff --git a/base/images/lightVisualizations.png b/base/images/lightVisualizations.png
new file mode 100644
index 0000000..a22c4b0
Binary files /dev/null and b/base/images/lightVisualizations.png differ
diff --git a/base/images/more lights no blur.png b/base/images/more lights no blur.png
new file mode 100644
index 0000000..458ad47
Binary files /dev/null and b/base/images/more lights no blur.png differ
diff --git a/base/images/more lights plus length blur.png b/base/images/more lights plus length blur.png
new file mode 100644
index 0000000..b5c5b5f
Binary files /dev/null and b/base/images/more lights plus length blur.png differ
diff --git a/base/images/separable.png b/base/images/separable.png
new file mode 100644
index 0000000..fdfd50d
Binary files /dev/null and b/base/images/separable.png differ
diff --git a/base/res/cornell/cornell_box.mtl b/base/res/cornell/cornell_box.mtl
index aae8474..57ccf45 100644
--- a/base/res/cornell/cornell_box.mtl
+++ b/base/res/cornell/cornell_box.mtl
@@ -12,13 +12,16 @@ newmtl green
Ka 0 0 0
Kd 0 1 0
Ks 0 0 0
+Ns 15.0
newmtl blue
Ka 0 0 0
Kd 0 0 1
Ks 0 0 0
+Ns 25.0
newmtl light
Ka 20 20 20
-Kd 1 1 1
+Kd 12 12 12
Ks 0 0 0
+
diff --git a/base/res/cornell/cornell_box.obj b/base/res/cornell/cornell_box.obj
index 43e021f..ec539c7 100644
--- a/base/res/cornell/cornell_box.obj
+++ b/base/res/cornell/cornell_box.obj
@@ -77,7 +77,7 @@ v 556.0 548.8 0.0
f -4 -3 -2 -1
o short_block
-usemtl white
+usemtl blue
v 130.0 165.0 65.0
v 82.0 165.0 225.0
diff --git a/base/res/shaders/ambient.frag b/base/res/shaders/ambient.frag
index 69b878c..99bd1e3 100644
--- a/base/res/shaders/ambient.frag
+++ b/base/res/shaders/ambient.frag
@@ -63,8 +63,8 @@ vec3 sampleNrm(vec2 texcoords) {
}
//Helper function to automicatlly sample and unpack positions
-vec3 samplePos(vec2 texcoords) {
- return texture(u_Positiontex,texcoords).xyz;
+vec4 samplePos(vec2 texcoords) {
+ return texture(u_Positiontex,texcoords);
}
//Helper function to automicatlly sample and unpack positions
@@ -99,7 +99,12 @@ void main() {
float lin_depth = linearizeDepth(exp_depth,u_Near,u_Far);
vec3 normal = sampleNrm(fs_Texcoord);
- vec3 position = samplePos(fs_Texcoord);
+
+ vec4 pos = samplePos(fs_Texcoord);
+ vec3 position = pos.xyz;
+ // Shininess and glow was packed
+ float shininess = 2.0*floor(pos.w/2.0);
+
vec3 color = sampleCol(fs_Texcoord);
vec3 light = u_Light.xyz;
float strength = u_Light.w;
@@ -108,7 +113,16 @@ void main() {
} else {
float ambient = u_LightIl;
float diffuse = max(0.0, dot(normalize(light),normal));
- out_Color = vec4(color*(strength*diffuse + ambient),1.0f);
+
+ // position is in eye space, so view vector should be negative position?
+ vec3 halfVec = normalize(normalize(-position) + normalize(light));
+ float spec = clamp(dot(halfVec, normal),0.0,1.0);
+ if(shininess < 0.0001)
+ spec = 0.0;
+ else
+ spec = pow(spec,shininess);
+
+ out_Color = vec4(0.0);//vec4(color*(strength*(diffuse + spec) + ambient),1.0f);
}
return;
}
diff --git a/base/res/shaders/directional.frag b/base/res/shaders/directional.frag
index a34daab..d8b6752 100644
--- a/base/res/shaders/directional.frag
+++ b/base/res/shaders/directional.frag
@@ -63,8 +63,8 @@ vec3 sampleNrm(vec2 texcoords) {
}
//Helper function to automicatlly sample and unpack positions
-vec3 samplePos(vec2 texcoords) {
- return texture(u_Positiontex,texcoords).xyz;
+vec4 samplePos(vec2 texcoords) {
+ return texture(u_Positiontex,texcoords);
}
//Helper function to automicatlly sample and unpack positions
@@ -99,12 +99,24 @@ void main() {
float lin_depth = linearizeDepth(exp_depth,u_Near,u_Far);
vec3 normal = sampleNrm(fs_Texcoord);
- vec3 position = samplePos(fs_Texcoord);
+ vec4 pos = samplePos(fs_Texcoord);
+
+ vec3 position = pos.xyz;
+ // Shininess and glow was packed
+ float shininess = 2.0*floor(pos.w/2.0);
+
vec3 color = sampleCol(fs_Texcoord);
vec3 light = u_Light.xyz;
float lightRadius = u_Light.w;
float diffuse = max(0.0, dot(normalize(light),normal));
+
+
+ // position is in eye space, so view vector should be negative position?
+ vec3 halfVec = normalize(normalize(-position) + normalize(light));
+ float spec = dot(halfVec, normal);
+ spec = pow(spec,shininess);
+
out_Color = vec4(color*u_LightIl*diffuse,1.0f);
}
diff --git a/base/res/shaders/pass.frag b/base/res/shaders/pass.frag
index e37dcbf..6f87d95 100644
--- a/base/res/shaders/pass.frag
+++ b/base/res/shaders/pass.frag
@@ -2,6 +2,8 @@
uniform float u_Far;
uniform vec3 u_Color;
+uniform float u_Glow;
+uniform float u_Shininess;
in vec3 fs_Normal;
in vec4 fs_Position;
@@ -13,6 +15,8 @@ out vec4 out_Color;
void main(void)
{
out_Normal = vec4(normalize(fs_Normal),0.0f);
- out_Position = vec4(fs_Position.xyz,1.0f); //Tuck position into 0 1 range
- out_Color = vec4(u_Color,1.0);
+ // shininess can fit in 31 bits, glow just really needs 1 bit
+ out_Position = vec4(fs_Position.xyz,(u_Shininess * 2 + u_Glow)); //Tuck position into 0 1 range
+ // Potentially use alpha channel for something useful like emmission light or not! : for Glow shading
+ out_Color = vec4(u_Color, 1.0);
}
diff --git a/base/res/shaders/point.frag b/base/res/shaders/point.frag
index 98b90e0..2aac447 100644
--- a/base/res/shaders/point.frag
+++ b/base/res/shaders/point.frag
@@ -31,6 +31,7 @@ uniform int u_DisplayType;
uniform int u_ScreenWidth;
uniform int u_ScreenHeight;
+uniform vec3 u_LightCol;
uniform vec4 u_Light;
uniform float u_LightIl;
@@ -62,8 +63,8 @@ vec3 sampleNrm(vec2 texcoords) {
}
//Helper function to automicatlly sample and unpack positions
-vec3 samplePos(vec2 texcoords) {
- return texture(u_Positiontex,texcoords).xyz;
+vec4 samplePos(vec2 texcoords) {
+ return texture(u_Positiontex,texcoords);
}
//Helper function to automicatlly sample and unpack positions
@@ -98,18 +99,38 @@ void main() {
float lin_depth = linearizeDepth(exp_depth,u_Near,u_Far);
vec3 normal = sampleNrm(fs_Texcoord);
- vec3 position = samplePos(fs_Texcoord);
+ vec4 pos = samplePos(fs_Texcoord);
+
+ vec3 position = pos.xyz;
+ // Shininess and glow was packed
+ float shininess = 2.0*floor(pos.w/2.0);
+
vec3 color = sampleCol(fs_Texcoord);
vec3 light = u_Light.xyz;
float lightRadius = u_Light.w;
out_Color = vec4(0,0,0,1.0);
if( u_DisplayType == DISPLAY_LIGHTS )
{
- //Put some code here to visualize the fragment associated with this point light
+ out_Color = vec4(0.0125,0.0325,0.0425,1.0);
+ //Put some code here to visualize the fragment associated with this point light
}
else
{
- //Put some code here to actually compute the light from the point light
+ vec3 posToLight = light-position;
+ float diffuse = max(0.0, dot(normalize(posToLight),normal));
+
+ // position is in eye space, so view vector should be negative position?
+ vec3 halfVec = normalize(normalize(vec3(0.0,0.0,-0.0)-position) + normalize(posToLight));
+ float spec = clamp(dot(halfVec, normal),0.0,1.0);
+ if(shininess < 0.0001)
+ spec = 0.0;
+ else
+ spec = pow(spec,shininess);
+
+ float luminance = u_LightIl * max(0.0,(1.0 - length(posToLight)/lightRadius));
+ out_Color = vec4(1.0* luminance*u_LightCol*color*(lightRadius*(diffuse)) + vec3(1.0,1.0,1.0) *(luminance * u_LightCol * lightRadius * spec),1.0f);
+ //out_Color = vec4(1.0,0.0,0.0,1.0);
+ //Put some code here to actually compute the light from the point light
}
return;
}
diff --git a/base/res/shaders/post.frag b/base/res/shaders/post.frag
index 2bf5afc..6cb8971 100644
--- a/base/res/shaders/post.frag
+++ b/base/res/shaders/post.frag
@@ -11,24 +11,50 @@
#define DISPLAY_TOTAL 4
#define DISPLAY_LIGHTS 5
+#define ARG_PASS 0
+#define ARG_BLOOM_SELECT 1
+#define ARG_BLOOM_2D 2
+#define ARG_GLOW_SELECT 3
+#define ARG_GLOW_2D 4
+#define ARG_BLUR_X 5
+#define ARG_BLUR_Y 6
+#define ARG_VIGNETTE 7
+#define ARG_SSAO_SCALE 8
+#define ARG_MULT_BLEND 9
+#define ARG_ADD_BLEND 10
/////////////////////////////////////
// Uniforms, Attributes, and Outputs
////////////////////////////////////
+
+#define PI 3.14159265
+
+#define NUM_SAMPLES 4
+#define NUM_SPIRAL_TURNS 4
+
uniform sampler2D u_Posttex;
+uniform sampler2D u_Posttex2;
+uniform sampler2D u_Positiontex;
+uniform sampler2D u_Normaltex;
uniform sampler2D u_RandomNormaltex;
uniform sampler2D u_RandomScalartex;
uniform int u_ScreenWidth;
uniform int u_ScreenHeight;
+uniform int u_Mode;
+uniform int u_kernelWidthX;
+uniform int u_kernelWidthY;
+
in vec2 fs_Texcoord;
out vec4 out_Color;
///////////////////////////////////////
+#define BLOOM_THRESHOLD 0.8
-
+vec2 texel = vec2(1.0/float(u_ScreenWidth),1.0/float(u_ScreenHeight));
+float oneOverSqrtTwoPi = 0.3989422804;
uniform float zerothresh = 1.0f;
uniform float falloff = 0.1f;
@@ -38,9 +64,29 @@ uniform float falloff = 0.1f;
// UTILITY FUNCTIONS
/////////////////////////////////////
+// Normal Distribution weighting
+float gaussianWeight(float dx, float radius)
+{
+ //float width = (2.0 * radius + 1.0);
+ //return 1.0/(width);
+
+ if (radius < 0.0001) return 1.0;
+
+ float sigma = radius/2.0;
+ return (oneOverSqrtTwoPi / sigma) * exp(- (dx*dx)/(2.0 * sigma * sigma) );
+}
+
//Helper function to automicatlly sample and unpack positions
-vec3 sampleCol(vec2 texcoords) {
- return texture(u_Posttex,texcoords).xyz;
+vec4 sampleCol(vec2 texcoords) {
+ return texture(u_Posttex,texcoords).xyzw;
+}
+
+vec4 samplePos(vec2 texcoords) {
+ return texture(u_Positiontex,texcoords);
+}
+
+vec4 sampleNorm(vec2 texcoords) {
+ return texture(u_Normaltex,texcoords);
}
//Get a random normal vector given a screen-space texture coordinate
@@ -60,15 +106,183 @@ float getRandomScalar(vec2 texcoords) {
texcoords.t*u_ScreenHeight/sz.y)).r;
}
+vec3 pickUpAngle(int sampleNumber, float spinAngle)
+{
+ float radiusSS;
+ // radius relative to radiusSS
+ float alpha = (float(sampleNumber) + 0.5) * (1.0 / float(NUM_SAMPLES));
+ float angle = alpha * (float(NUM_SPIRAL_TURNS) * 6.28) + spinAngle;
+
+ radiusSS = alpha;
+ return vec3(cos(angle), sin(angle), radiusSS);
+}
+
+vec3 getOffsetPosition(vec2 unitOffset, float radiusSS)
+{
+ vec2 offsetTexcoord = fs_Texcoord + radiusSS * unitOffset * (1.0 / sqrt(float(u_ScreenWidth * u_ScreenHeight)));
+ return samplePos(offsetTexcoord).xyz;
+}
+
+float sampleAO(vec3 position, vec3 normal, float radius, int index, float angle)
+{
+ float epsilon = 0.0001;
+ float radius2 = radius*radius;
+ vec3 ret = pickUpAngle(index, angle);
+ vec2 unitOffset = ret.xy;
+ float radiusSS = ret.z * radius;
+
+ vec3 Q = getOffsetPosition(unitOffset, radiusSS);
+ vec3 v = Q - position;
+
+ float bias = 0.001;
+
+ float vv = dot(v,v);
+ float vn = dot(v,normal) - bias;
+
+ float invRadius2 = 1.0 / radius2;
+ return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn, 0.0);
+}
///////////////////////////////////
// MAIN
//////////////////////////////////
const float occlusion_strength = 1.5f;
void main() {
- vec3 color = sampleCol(fs_Texcoord);
- float gray = dot(color, vec3(0.2125, 0.7154, 0.0721));
- float vin = min(2*distance(vec2(0.5), fs_Texcoord), 1.0);
- out_Color = vec4(mix(pow(color,vec3(1.0/1.8)),vec3(gray),vin), 1.0);
+ vec4 color = sampleCol(fs_Texcoord) + vec4(vec3(0.0),1.0);
+
+ float radiusX = float(u_kernelWidthX);
+ float radiusY = float(u_kernelWidthY);
+
+ if(u_Mode==ARG_PASS)
+ {
+ out_Color = color;
+ }
+ else if(u_Mode == ARG_VIGNETTE)
+ {
+ float gray = dot(color.xyz, vec3(0.2125, 0.7154, 0.0721));
+ float vin = min(2*distance(vec2(0.5), fs_Texcoord), 1.0);
+ out_Color = vec4(mix(pow(color.xyz,vec3(1.0/1.8)),vec3(gray),vin), 1.0);
+ }
+ else if(u_Mode == ARG_BLOOM_2D)
+ {
+ // We only want to add the bloom, not reduce value of current pixel if it is already below threshold!
+ vec3 finalColor = color.xyz;
+
+ // Bloom Effect: simple 2D Lookup kernel
+ for(int x = -u_kernelWidthX; x <= u_kernelWidthX ; x++)
+ {
+ for(int y = -u_kernelWidthY; y <= u_kernelWidthY; y++)
+ {
+ if(x==0 && y==0) continue;
+ // Simple test case of blur
+ vec2 currentTexCoord = fs_Texcoord + vec2(texel.s * x, texel.t * y);
+ vec4 bColor = sampleCol(currentTexCoord);
+ vec3 bloomColor = bColor.xyz;
+
+ if(bloomColor.r > BLOOM_THRESHOLD || bloomColor.g > BLOOM_THRESHOLD || bloomColor.b > BLOOM_THRESHOLD)// || bloomColor.)
+ finalColor += gaussianWeight(float(x),radiusX) * gaussianWeight(float(y),radiusY) * bloomColor;
+ }
+ }
+ out_Color = vec4(finalColor,1.0);
+ }
+ else if(u_Mode == ARG_BLOOM_SELECT)
+ {
+ vec3 bloomColor = sampleCol(fs_Texcoord).xyz;
+ if(bloomColor.r > BLOOM_THRESHOLD || bloomColor.g > BLOOM_THRESHOLD || bloomColor.b > BLOOM_THRESHOLD)// || bloomColor.)
+ out_Color = vec4(bloomColor.xyz,1.0);
+ }
+ else if(u_Mode == ARG_GLOW_2D)
+ {
+
+ vec3 finalColor = color.xyz;
+
+ // Bloom Effect: simple 2D Lookup kernel
+ for(int x = -u_kernelWidthX; x <= u_kernelWidthX ; x++)
+ {
+ for(int y = -u_kernelWidthY; y <= u_kernelWidthY; y++)
+ {
+ if(x==0 && y==0) continue;
+
+ // Simple test case of blur
+ vec2 currentTexCoord = fs_Texcoord + vec2(texel.s * x, texel.t * y);
+ vec4 glowColor = sampleCol(currentTexCoord);
+ float glow = samplePos(currentTexCoord).w;
+ // glow has been packed.
+ glow = glow- 2.0 * floor(glow/2.0);
+ if(glow > 0.0)
+ finalColor += gaussianWeight(float(x),radiusX) * gaussianWeight(float(y),radiusY) * glowColor.xyz;
+ }
+ }
+ out_Color = vec4(finalColor,1.0);
+ }
+ else if(u_Mode == ARG_GLOW_SELECT)
+ {
+ vec4 glowColor = sampleCol(fs_Texcoord);
+ float glow = samplePos(fs_Texcoord).w;
+ glow = glow- 2.0 * floor(glow/2.0);
+ if(glow > 0.0)
+ out_Color = vec4(glowColor.xyz,1.0);
+ }
+ else if(u_Mode == ARG_BLUR_X)
+ {
+ vec3 finalColor = vec3(0.0);
+ for(int x = -u_kernelWidthX; x <= u_kernelWidthX ; x++)
+ {
+ int y=0;
+ vec2 currentTexCoord = fs_Texcoord + vec2(texel.s * x, texel.t * y);
+ vec4 blurColor = sampleCol(currentTexCoord);
+ finalColor += gaussianWeight(float(x),radiusX) * blurColor.xyz;
+ }
+ out_Color = vec4(finalColor,1.0);
+
+ }
+ else if(u_Mode == ARG_BLUR_Y)
+ {
+ vec3 finalColor = vec3(0.0);//color.xyz;
+ for(int y = -u_kernelWidthY; y <= u_kernelWidthY ; y++)
+ {
+ int x=0;
+ vec2 currentTexCoord = fs_Texcoord + vec2(texel.s * x, texel.t * y);
+ vec4 blurColor = sampleCol(currentTexCoord);
+ finalColor += gaussianWeight(float(y),radiusY) * blurColor.xyz;
+ }
+ out_Color = vec4(finalColor,1.0);
+ }
+ else if(u_Mode == ARG_SSAO_SCALE)
+ {
+ // http://graphics.cs.williams.edu/papers/SAOHPG12/
+ vec3 position = samplePos(fs_Texcoord).xyz;
+ vec3 normal = sampleNorm(fs_Texcoord).xyz;
+
+ vec2 randTexCoord = vec2(getRandomScalar(fs_Texcoord),getRandomScalar(fs_Texcoord + fs_Texcoord*fs_Texcoord));
+ vec3 sampleNoise = normalize(getRandomNormal(randTexCoord));
+ //vec3 randNormal = normalize(getRandomNormal(fs_Texcoord));
+ float randomPatternAngle = 2.0 * PI * sampleNoise.x;
+
+ float radius = u_kernelWidthX;
+ float occlusion = 0.0;
+
+ for(int i=0; i < NUM_SAMPLES; ++i)
+ {
+ occlusion += sampleAO(position,normal,radius,i,randomPatternAngle);
+ }
+ float intensity = 10.0;
+ occlusion = 1.0 - occlusion / (4.0 * float(NUM_SAMPLES));
+ occlusion = clamp(pow(occlusion, 1.0 + intensity),0.0,1.0);
+ out_Color = vec4(vec3(occlusion),1.0);
+ //out_Color = vec4(vec3(randNormal),1.0);
+ }
+ else if(u_Mode == ARG_ADD_BLEND)
+ {
+ vec3 col1 = texture(u_Posttex,fs_Texcoord).xyz;
+ vec3 col2 = texture(u_Posttex2,fs_Texcoord).xyz;
+ out_Color = clamp(vec4(col1 + col2, 1.0),0.0,1.0);
+ }
+ else if(u_Mode == ARG_MULT_BLEND)
+ {
+ vec3 col1 = texture(u_Posttex,fs_Texcoord).xyz;
+ vec3 col2 = texture(u_Posttex2,fs_Texcoord).xyz;
+ out_Color = clamp(vec4(col1 * col2, 1.0),0.0,1.0);
+ }
return;
}
diff --git a/base/src/main.cpp b/base/src/main.cpp
index 88aa6bb..75c0516 100644
--- a/base/src/main.cpp
+++ b/base/src/main.cpp
@@ -25,6 +25,40 @@ const float PI = 3.14159f;
int width, height;
+/***** Uniform variables that are interaction controllable *****/
+int kernelWidthX = 3;
+int kernelWidthY = 3;
+int postProcessMode = 0;
+int numLights = 12345;
+vectorlightPos;
+vectorlightCol;
+vector lightInt;
+void setupLights()
+{
+ lightPos.resize(0);
+ lightInt.resize(0);
+ for(int iter=0; iter NEARP)
@@ -634,6 +755,7 @@ void draw_light(vec3 pos, float strength, mat4 sc, mat4 vp, float NEARP) {
return;
}
light.w = radius;
+ glUniform3fv(glGetUniformLocation(point_prog, "u_LightCol"), 1, &(col[0]));
glUniform4fv(glGetUniformLocation(point_prog, "u_Light"), 1, &(light[0]));
glUniform1f(glGetUniformLocation(point_prog, "u_LightIl"), strength);
@@ -684,15 +806,47 @@ void updateDisplayText(char * disp) {
}
}
+void updateModeText(char * mode) {
+ switch(postProcessMode){
+ case(POST_PASS):
+ sprintf(mode, "Pass Through");
+ break;
+ case(POST_BLOOM_2D):
+ sprintf(mode, "Bloom 2D Kernel");
+ break;
+ case(POST_BLOOM_SEPARATED):
+ sprintf(mode, "Bloom Separated Kernel");
+ break;
+ case(POST_GLOW_2D):
+ sprintf(mode, "Glow 2D Kernel");
+ break;
+ case(POST_GLOW_SEPARATED):
+ sprintf(mode, "Glow Separated Kernel");
+ break;
+ case(POST_VIGNETTE):
+ sprintf(mode, "Vignette Kernel");
+ break;
+ case(POST_SSAO):
+ sprintf(mode, "SSAO: Scalable");
+ break;
+ case(POST_SSAO_BLEND):
+ sprintf(mode, "SSAO: Scalable Blended");
+ break;
+ }
+}
+
int frame = 0;
int currenttime = 0;
int timebase = 0;
char title[1024];
char disp[1024];
+char mode[1024];
char occl[1024];
void updateTitle() {
updateDisplayText(disp);
+
+ updateModeText(mode);
//calculate the frames per second
frame++;
@@ -702,7 +856,7 @@ void updateTitle() {
//check if a second has passed
if (currenttime - timebase > 1000)
{
- sprintf(title, "CIS565 OpenGL Frame | %s FPS: %4.2f", disp, frame*1000.0/(currenttime-timebase));
+ sprintf(title, "CIS565 OpenGL Frame | %s FPS: %4.2f |%d %s | Blur:%dx%d", disp, frame*1000.0/(currenttime-timebase),postProcessMode, mode, 2*kernelWidthX+1, 2*kernelWidthY+1);
//sprintf(title, "CIS565 OpenGL Frame | %4.2f FPS", frame*1000.0/(currenttime-timebase));
glutSetWindowTitle(title);
timebase = currenttime;
@@ -710,6 +864,61 @@ void updateTitle() {
}
}
+void setupPostFrameBuffer(int fboID, GLuint inputTexture, int postProcessEffect)
+{
+ bindFBO(fboID);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_TEXTURE_2D);
+ glUseProgram(post_prog);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, inputTexture);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Posttex"),0);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, normalTexture);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Normaltex"),2);
+
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D,positionTexture);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Positiontex"),3);
+
+ glActiveTexture(GL_TEXTURE4);
+ glBindTexture(GL_TEXTURE_2D, random_normal_tex);
+ glUniform1i(glGetUniformLocation(post_prog, "u_RandomNormaltex"),4);
+
+ glActiveTexture(GL_TEXTURE5);
+ glBindTexture(GL_TEXTURE_2D, random_scalar_tex);
+ glUniform1i(glGetUniformLocation(post_prog, "u_RandomScalartex"),5);
+
+ glUniform1i(glGetUniformLocation(post_prog, "u_ScreenHeight"), height);
+ glUniform1i(glGetUniformLocation(post_prog, "u_ScreenWidth"), width);
+
+ glUniform1i(glGetUniformLocation(post_prog, "u_kernelWidthX"),kernelWidthX);
+ glUniform1i(glGetUniformLocation(post_prog, "u_kernelWidthY"),kernelWidthY);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Mode"),postProcessEffect);
+
+}
+
+void setupBlendFrameBuffer(int outputFBO, GLuint inputTex1, GLuint inputTex2)
+{
+ bindFBO(outputFBO);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_TEXTURE_2D);
+ glUseProgram(post_prog);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, inputTex1);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Posttex"),0);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, inputTex2);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Posttex2"),1);
+
+ glUniform1i(glGetUniformLocation(post_prog, "u_Mode"),ARG_ADD_BLEND);
+
+}
+
bool doIScissor = true;
void display(void)
{
@@ -739,8 +948,19 @@ void display(void)
0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.5, 0.5, 0.0, 1.0);
-
- draw_light(vec3(2.5, -2.5, 5.0), 0.50, sc, vp, NEARP);
+
+ for( float i = 0.1 ; i <= 6.0 ; i+=0.85)
+ for(float j = 0.1 ; j <= 6.0 ; j+=0.85)
+ for(float k = 0.1 ; k <= 6.0 ; k+=0.85)
+ draw_light(vec3(i, -j, k), vec3(1.0,1.0,1.0), 0.75, sc, vp, NEARP);
+ /*
+ //setupLights();
+ for(int l = 0; l < numLights; l++)
+ {
+ draw_light(lightPos[l],lightCol[l],lightInt[l],sc,vp,NEARP);
+ }
+ */
+ //draw_light(vec3(2.5, -2.5, 4.0), 3.00, sc, vp, NEARP);
glDisable(GL_SCISSOR_TEST);
vec4 dir_light(0.1, 1.0, 1.0, 0.0);
@@ -761,15 +981,79 @@ void display(void)
glDisable(GL_BLEND);
setTextures();
+
+ GLuint finalTexture = postTexture;
+ GLuint finalBlendTexture = postTexture3;
+ int finalPostProcess = ARG_VIGNETTE;
+
+ switch(postProcessMode)
+ {
+ case POST_PASS:
+ finalPostProcess = ARG_PASS;
+ break;
+ case POST_BLOOM_2D:
+ finalPostProcess = ARG_BLOOM_2D;
+ break;
+ case POST_GLOW_2D:
+ finalPostProcess = ARG_GLOW_2D;
+ break;
+ case POST_VIGNETTE:
+ finalPostProcess = ARG_VIGNETTE;
+ break;
+ case POST_BLOOM_SEPARATED:
+ setupPostFrameBuffer(2,postTexture,ARG_BLOOM_SELECT);
+ draw_quad();
+ setupPostFrameBuffer(3,postTexture2,ARG_BLUR_X);
+ draw_quad();
+ setupPostFrameBuffer(2,postTexture3,ARG_BLUR_Y);
+ draw_quad();
+ finalPostProcess = ARG_ADD_BLEND;
+ finalTexture = postTexture;
+ finalBlendTexture = postTexture2;
+ break;
+ case POST_GLOW_SEPARATED:
+ setupPostFrameBuffer(2,postTexture,ARG_GLOW_SELECT);
+ draw_quad();
+ setupPostFrameBuffer(3,postTexture2,ARG_BLUR_X);
+ draw_quad();
+ setupPostFrameBuffer(2,postTexture3,ARG_BLUR_Y);
+ draw_quad();
+ finalPostProcess = ARG_ADD_BLEND;
+ finalTexture = postTexture;
+ finalBlendTexture = postTexture2;
+ break;
+ case POST_SSAO:
+ finalPostProcess = ARG_SSAO_SCALE;
+ break;
+ case POST_SSAO_BLEND:
+ setupPostFrameBuffer(2,postTexture,ARG_SSAO_SCALE);
+ draw_quad();
+ finalPostProcess = ARG_MULT_BLEND;
+ finalBlendTexture = postTexture2;
+ break;
+ }
+
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glUseProgram(post_prog);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, postTexture);
+ glBindTexture(GL_TEXTURE_2D, finalTexture);
glUniform1i(glGetUniformLocation(post_prog, "u_Posttex"),0);
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, finalBlendTexture);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Posttex2"),1);
+
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_2D, normalTexture);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Normaltex"),2);
+
+ glActiveTexture(GL_TEXTURE3);
+ glBindTexture(GL_TEXTURE_2D,positionTexture);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Positiontex"),3);
+
glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D, random_normal_tex);
glUniform1i(glGetUniformLocation(post_prog, "u_RandomNormaltex"),4);
@@ -780,7 +1064,13 @@ void display(void)
glUniform1i(glGetUniformLocation(post_prog, "u_ScreenHeight"), height);
glUniform1i(glGetUniformLocation(post_prog, "u_ScreenWidth"), width);
+
+ glUniform1i(glGetUniformLocation(post_prog, "u_kernelWidthX"),kernelWidthX);
+ glUniform1i(glGetUniformLocation(post_prog, "u_kernelWidthY"),kernelWidthY);
+ glUniform1i(glGetUniformLocation(post_prog, "u_Mode"),finalPostProcess);
+
draw_quad();
+
glEnable(GL_DEPTH_TEST);
updateTitle();
@@ -836,6 +1126,22 @@ void motion(int x, int y)
mouse_old_y = y;
}
+void specialKeyboard(int key, int x, int y){
+
+ switch(key)
+ {
+ case GLUT_KEY_UP: kernelWidthY++;
+ break;
+ case GLUT_KEY_DOWN: kernelWidthY += kernelWidthY > 0? -1 : 0;
+ break;
+ case GLUT_KEY_RIGHT:kernelWidthX++;
+ break;
+ case GLUT_KEY_LEFT: kernelWidthX += kernelWidthX > 0? -1 : 0;
+ break;
+ }
+
+}
+
void keyboard(unsigned char key, int x, int y) {
float tx = 0;
float ty = 0;
@@ -845,22 +1151,22 @@ void keyboard(unsigned char key, int x, int y) {
exit(0.0);
break;
case('w'):
- tz = 0.1;
+ tz = 0.3;
break;
case('s'):
- tz = -0.1;
+ tz = -0.3;
break;
case('d'):
- tx = -0.1;
+ tx = -0.3;
break;
case('a'):
- tx = 0.1;
+ tx = 0.3;
break;
case('q'):
- ty = 0.1;
+ ty = 0.3;
break;
case('z'):
- ty = -0.1;
+ ty = -0.3;
break;
case('1'):
display_type = DISPLAY_DEPTH;
@@ -880,6 +1186,12 @@ void keyboard(unsigned char key, int x, int y) {
case('0'):
display_type = DISPLAY_TOTAL;
break;
+ case(']'):
+ postProcessMode = (postProcessMode + 1) % POST_COUNT;
+ break;
+ case('['):
+ postProcessMode = postProcessMode > 1 ? (postProcessMode - 1) : 0;
+ break;
case('x'):
doIScissor ^= true;
break;
@@ -900,6 +1212,10 @@ void init() {
int main (int argc, char* argv[])
{
+
+ srand (time(NULL));
+ setupLights();
+
bool loadedScene = false;
for(int i=1; i
#include
+#include
#include
#include "tiny_obj_loader.h"
@@ -36,7 +37,9 @@ typedef struct {
std::vector texcoords;
std::vector indices;
std::string texname;
+ std::string meshName;
glm::vec3 color;
+ float shininess;
} mesh_t;
typedef struct {
@@ -48,6 +51,8 @@ typedef struct {
unsigned int vbo_texcoords;
glm::vec3 color;
std::string texname;
+ std::string meshName;
+ float shininess;
} device_mesh_t;
typedef struct {
@@ -86,6 +91,32 @@ enum Display {
DISPLAY_LIGHTS = 5
};
+enum PostProcess {
+ POST_PASS = 0,
+ POST_BLOOM_2D,
+ POST_BLOOM_SEPARATED,
+ POST_GLOW_2D,
+ POST_GLOW_SEPARATED,
+ POST_VIGNETTE,
+ POST_SSAO,
+ POST_SSAO_BLEND,
+ POST_COUNT
+};
+
+enum PostProcessArg {
+ ARG_PASS = 0,
+ ARG_BLOOM_SELECT,
+ ARG_BLOOM_2D,
+ ARG_GLOW_SELECT,
+ ARG_GLOW_2D,
+ ARG_BLUR_X,
+ ARG_BLUR_Y,
+ ARG_VIGNETTE,
+ ARG_SSAO_SCALE,
+ ARG_MULT_BLEND,
+ ARG_ADD_BLEND,
+};
+
char* loadFile(char *fname, GLint &fSize);
void printShaderInfoLog(GLint shader);
void printLinkInfoLog(GLint prog);
diff --git a/base/src/tiny_obj_loader/tiny_obj_loader.cc b/base/src/tiny_obj_loader/tiny_obj_loader.cc
index 7894e70..67f8536 100644
--- a/base/src/tiny_obj_loader/tiny_obj_loader.cc
+++ b/base/src/tiny_obj_loader/tiny_obj_loader.cc
@@ -269,7 +269,7 @@ void InitMaterial(material_t& material) {
material.transmittance[i] = 0.f;
material.emission[i] = 0.f;
}
- material.shininess = 1.f;
+ material.shininess = 0.f;
}
std::string LoadMtl (