Beispiel #1
0
        public void Setup(int cx, int cy)
        {
            this._cx = cx;
            this._cy = cy;

            // Version strings
            _version.Retrieve();

            // Get OpenGL extensions
            _extensions.Retrieve();

            // Debug callback
            _debug_callback.Init();

            // to do mesh buffer creator form multi indices mesh
            // use in-source wave font file?

            float[] attributes =
            {
                // left
                -1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
                -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
                -1.0f, -1.0f,  1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
                -1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 1.0f, 0.0f,

                // right
                1.0f,  -1.0f, -1.0f, 0.0f, 0.0f, 2.0f, 0.0f,
                1.0f,   1.0f, -1.0f, 0.0f, 1.0f, 2.0f, 0.0f,
                1.0f,   1.0f,  1.0f, 1.0f, 1.0f, 2.0f, 0.0f,
                1.0f,  -1.0f,  1.0f, 1.0f, 0.0f, 2.0f, 0.0f,

                // front
                -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 3.0f, 0.0f,
                1.0f,  -1.0f, -1.0f, 0.0f, 1.0f, 3.0f, 0.0f,
                1.0f,  -1.0f,  1.0f, 1.0f, 1.0f, 3.0f, 0.0f,
                -1.0f, -1.0f,  1.0f, 1.0f, 0.0f, 3.0f, 0.0f,

                // back
                1.0f,   1.0f, -1.0f, 0.0f, 0.0f, 4.0f, 0.0f,
                -1.0f,  1.0f, -1.0f, 0.0f, 1.0f, 4.0f, 0.0f,
                -1.0f,  1.0f,  1.0f, 1.0f, 1.0f, 4.0f, 0.0f,
                1.0f,   1.0f,  1.0f, 1.0f, 0.0f, 4.0f, 0.0f,

                // bottom
                -1.0f,  1.0f, -1.0f, 0.0f, 0.0f, 5.0f, 0.0f,
                1.0f,   1.0f, -1.0f, 0.0f, 1.0f, 5.0f, 0.0f,
                1.0f,  -1.0f, -1.0f, 1.0f, 1.0f, 5.0f, 0.0f,
                -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 5.0f, 0.0f,

                // top
                -1.0f, -1.0f,  1.0f, 0.0f, 0.0f, 6.0f, 0.0f,
                1.0f,  -1.0f,  1.0f, 0.0f, 1.0f, 6.0f, 0.0f,
                1.0f,   1.0f,  1.0f, 1.0f, 1.0f, 6.0f, 0.0f,
                -1.0f,  1.0f,  1.0f, 1.0f, 0.0f, 6.0f, 0.0f
            };

            uint[] indices =
            {
                0,   1,  2,  0,  2, 3,  // front
                4,   5,  6,  4,  6, 7,  // back
                8,   9, 10,  8, 10, 11, // left
                12, 13, 14, 12, 14, 15, // right
                16, 17, 18, 16, 18, 19, // bottom
                20, 21, 22, 20, 22, 23  // top
            };

            // create Vertex Array Object, Array Buffer Object and Element Array Buffer Object

            TVertexFormat[] format =
            {
                new TVertexFormat(0, 0, 3, 0, false),
                new TVertexFormat(0, 1, 4, 3, false),
            };

            _vao = openGLFactory.NewVertexArrayObject();
            _vao.AppendVertexBuffer(0, 7, attributes);
            _vao.Create(format, indices);
            _vao.Bind();

            // Create shader program

            string vert_shader = @"
            #version 460 core

            layout (location = 0) in vec3 inPos;
            layout (location = 1) in vec4 inAttr;

            out vec3  vertPos;
            out vec4  vertTex;
            out float highlight;

            layout (std430, binding = 1) buffer UB_MVP
            { 
                mat4 u_projection;
                mat4 u_view;
                mat4 u_model;
            };

            layout (std430, binding = 2) buffer UB_RUBIKS
            { 
                mat4 u_rubiks_model[27];
                int  u_cube_hit;
                int  u_side_hit;
            };

            void main()
            {
                vec4 tex     = inAttr;
                int  cube_i  = gl_InstanceID;
                int  color_i = int(tex.z + 0.5); 
                int  x_i     = cube_i % 3;
                int  y_i     = (cube_i % 9) / 3;
                int  z_i     = cube_i / 9;

                if ( color_i == 1 )
                    tex.z = x_i == 0 ? tex.z : 0.0;
                else if ( color_i == 2 )
                    tex.z = x_i == 2 ? tex.z : 0.0;
                else if ( color_i == 3 )
                    tex.z = y_i == 0 ? tex.z : 0.0;
                else if ( color_i == 4 )
                    tex.z = y_i == 2 ? tex.z : 0.0;
                else if ( color_i == 5 )
                    tex.z = z_i == 0 ? tex.z : 0.0;
                else if ( color_i == 6 )
                    tex.z = z_i == 2 ? tex.z : 0.0;

                mat4 model_view = u_view * u_model * u_rubiks_model[cube_i];
                vec4 vertex_pos = model_view * vec4(inPos, 1.0);

                vertPos     = vertex_pos.xyz;
                vertTex     = tex;
                //highlight   = tex.z > 0.5 && cube_i == u_cube_hit ? 1.0 : 0.0;	
                //highlight   = tex.z > 0.5 && color_i == u_side_hit ? 1.0 : 0.0;
                highlight   = tex.z > 0.5 && cube_i == u_cube_hit && color_i == u_side_hit ? 1.0 : 0.0;		

		        gl_Position = u_projection * vertex_pos;
            }";

            string frag_shader = @"
            #version 460 core

            in vec3  vertPos;
            in vec4  vertTex;
            in float highlight;

            out vec4 fragColor;

            vec4 color_table[7] = vec4[7](
                vec4(0.5, 0.5, 0.5, 1.0),
                vec4(1.0, 0.0, 0.0, 1.0),
                vec4(0.0, 1.0, 0.0, 1.0),
                vec4(0.0, 0.0, 1.0, 1.0),
                vec4(1.0, 0.5, 0.0, 1.0),
                vec4(1.0, 1.0, 0.0, 1.0),
                vec4(1.0, 0.0, 1.0, 1.0)
            );

            void main()
            {
                int color_i = int(vertTex.z + 0.5);

                vec4 color = color_table[color_i]; 
                color.rgb *= max(0.5, highlight);

                fragColor  = color;
            }";

            this._prog = openGLFactory.VertexAndFragmentShaderProgram(vert_shader, frag_shader);
            this._prog.Generate();

            // matrices

            this._view = Matrix4.LookAt(0.0f, -4.0f, 0.0f, 0, 0, 0, 0, 0, 1);

            float angle  = 70.0f * (float)Math.PI / 180.0f;
            float aspect = (float)this._cx / (float)this._cy;

            this._projection = Matrix4.CreatePerspectiveFieldOfView(angle, aspect, 0.1f, 100.0f);

            // Model view projection shader storage block objects and buffers
            this._mvp_data = new TMVP(Matrix4.Identity, this._view, this._projection);
            this._mvp_ssbo = openGLFactory.NewStorageBuffer();
            this._mvp_ssbo.Create(ref this._mvp_data);
            this._mvp_ssbo.Bind(1);

            T_RUBIKS_DATA rubiks_data = rubiks_data = new T_RUBIKS_DATA();

            this._rubiks_ssbo = openGLFactory.NewStorageBuffer();
            this._rubiks_ssbo.Create(ref rubiks_data);
            this._rubiks_ssbo.Bind(2);

            TLightSource light_source = new TLightSource(new Vector4(-1.0f, -0.5f, -2.0f, 0.0f), 0.2f, 0.8f, 0.8f, 10.0f);

            this._light_ssbo = openGLFactory.NewStorageBuffer();
            this._light_ssbo.Create(ref light_source);
            this._light_ssbo.Bind(3);

            this._depth_pack_buffer = openGLFactory.NewPixelPackBuffer();
            this._depth_pack_buffer.Create <float>();

            // states

            GL.Viewport(0, 0, this._cx, this._cy);
            //GL.ClearColor(System.Drawing.Color.Beige);
            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            GL.Enable(EnableCap.DepthTest);
            GL.Enable(EnableCap.CullFace);
            GL.FrontFace(FrontFaceDirection.Ccw);
            GL.CullFace(CullFaceMode.Back);

            // controller

            var spin = new ModelSpinningControls(
                () => { return(this._period); },
                () => { return(new float[] { 0, 0, (float)this._cx, (float)this._cy }); },
                () => { return(this._view); }
                );

            spin.SetAttenuation(1.0f, 0.05f, 0.0f);
            _controls = new RubiksMouseControlsProxy(spin);

            float offset = 2.0f * 1.1f;
            float scale  = 1.0f / 3.0f;

            this._rubiks_cube = new RubiksControls(
                () => { return(this._period); },
                offset, scale
                );

            int shuffles = 11;

            this._rubiks_cube.Shuffle(shuffles);
            double time_s = 1.0;

            _rubiks_cube.AnimationTime = time_s;
        }
        public void Setup(int cx, int cy)
        {
            this._cx = cx;
            this._cy = cy;

            // Version strings
            _version.Retrieve();

            // Get OpenGL extensions
            _extensions.Retrieve();

            // Debug callback
            _debug_callback.Init();

            // create Vertex Array Object, Array Buffer Object and Element Array Buffer Object

            (float[] attributes, uint[] indices) = new Cube().Create();
            TVertexFormat[] format =
            {
                new TVertexFormat(0, 0, 3, 0, false),
                new TVertexFormat(0, 1, 3, 3, false),
                new TVertexFormat(0, 2, 2, 6, false),
                //new TVertexFormat(0, 2, 4, 8, false),
            };

            _cube_vao = openGLFactory.NewVertexArrayObject();
            _cube_vao.AppendVertexBuffer(0, 12, attributes);
            _cube_vao.Create(format, indices);
            _cube_vao.Bind();

            // Create textures objects

            _tbos = new List <ITexture>();
            _tbos.Add(openGLFactory.NewTexture());
            _tbos[0].Create2D(_image_cx, _image_cy, ITexture.Format.RGBA_8);
            _tbos.Add(openGLFactory.NewTexture());
            _tbos[1].Create2D(_image_cx, _image_cy, ITexture.Format.RGBA_F32);
            _tbos.Add(openGLFactory.NewTexture());
            _tbos[2].Create2D(_image_cx, _image_cy, ITexture.Format.RGBA_F32);
            GL.TextureParameter(_tbos[2].Object, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TextureParameter(_tbos[2].Object, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);

            // Create generators

            this._generators = new List <TextureGenerator>();
            this._generators.Add(new TextureGenerator(openGLFactory, TextureGenerator.TType.texture_test1, new ITexture[] { _tbos[0] }));
            this._generators.Add(new TextureGenerator(openGLFactory, TextureGenerator.TType.heightmap_test1, new ITexture[] { _tbos[1] }));
            this._generators.Add(new TextureGenerator(openGLFactory, TextureGenerator.TType.cone_step_map, new ITexture[] { _tbos[2] }, new ITexture[] { _tbos[1] }));

            // Create textures

            foreach (var generator in this._generators)
            {
                generator.Generate();
            }

            // Create shader program

            string vert_shader = @"#version 460 core
            
            layout (location = 0) in vec3 inPos;
            layout (location = 1) in vec3 inNV;
            layout (location = 2) in vec2 inUV;

            out TVertexData
            {
                vec3  pos;
                vec3  nv;
                vec2  uv;
                float clip;
            } out_data;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            layout(location=1) uniform vec4 u_clipPlane;

            void main()
            {
                vec3 modelNV   = mat3( mvp.model ) * normalize( inNV );
                out_data.nv    = mat3( mvp.view ) * modelNV;
                out_data.uv    = inUV;
                vec4 worldPos  = mvp.model * vec4( inPos, 1.0 );
                vec4 viewPos   = mvp.view * worldPos;
                out_data.pos   = viewPos.xyz / viewPos.w;
                gl_Position    = mvp.proj * viewPos;
                vec4 clipPlane = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w);
                out_data.clip  = dot(worldPos, clipPlane);
            }";

            string frag_shader = @"#version 460 core
            //#define NORMAL_MAP_TEXTURE
            #define NORMAL_MAP_QUALITY 1

            in TVertexData
            {
                vec3  pos;
                vec3  nv;
                vec2  uv;
                float clip;
            } in_data;

            out vec4 fragColor;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            layout(location=1) uniform vec4 u_clipPlane;

            layout(std430, binding = 2) buffer TLight
            {
                vec4  u_lightDir;
                float u_ambient;
                float u_diffuse;
                float u_specular;
                float u_shininess;
            } light_data;

            layout(binding=1)  uniform sampler2D u_texture;
            layout(binding=2)  uniform sampler2D u_displacement_map;
            layout(location=2) uniform float     u_displacement_scale;
            layout(location=3) uniform vec2      u_parallax_quality;

            #if defined(NORMAL_MAP_TEXTURE)
            uniform sampler2D u_normal_map;
            #endif

            vec2 GetHeightAndCone( in vec2 texCoords )
            {
                vec2 h_and_c = texture( u_displacement_map, texCoords ).rg;
                return clamp( h_and_c, 0.0, 1.0 );
            }

            vec4 CalculateNormal( in vec2 texCoords )
            {
            #if defined(NORMAL_MAP_TEXTURE)
                float height = GetHeight( texCoords );
                vec3  tempNV = texture( u_normal_map, texCoords ).xyz * 2.0 / 1.0;
                return vec4( normalize( tempNV ), height );
            #else
                vec2 texOffs = 1.0 / textureSize( u_displacement_map, 0 ).xy;
                vec2 scale   = 1.0 / texOffs;
            #if NORMAL_MAP_QUALITY > 1
                float hx[9];
                hx[0] = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0, -1.0) ).r;
                hx[1] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r;
                hx[2] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, -1.0) ).r;
                hx[3] = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0,  0.0) ).r;
                hx[4] = texture( u_displacement_map, texCoords.st ).r;
                hx[5] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, 0.0) ).r;
                hx[6] = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0, 1.0) ).r;
                hx[7] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, 1.0) ).r;
                hx[8] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, 1.0) ).r;
                vec2  deltaH = vec2(hx[0]-hx[2] + 2.0*(hx[3]-hx[5]) + hx[6]-hx[8], hx[0]-hx[6] + 2.0*(hx[1]-hx[7]) + hx[2]-hx[8]); 
                float h_mid  = hx[4];
            #elif NORMAL_MAP_QUALITY > 0
                float h_mid  = texture( u_displacement_map, texCoords.st ).r;
                float h_xa   = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0,  0.0) ).r;
                float h_xb   = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0,  0.0) ).r;
                float h_ya   = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r;
                float h_yb   = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0,  1.0) ).r;
                vec2  deltaH = vec2(h_xa-h_xb, h_ya-h_yb); 
            #else
                vec4  heights = textureGather( u_displacement_map, texCoords, 0 );
                vec2  deltaH  = vec2(dot(heights, vec4(1.0, -1.0, -1.0, 1.0)), dot(heights, vec4(-1.0, -1.0, 1.0, 1.0)));
                float h_mid   = heights.w; 
            #endif
                return vec4( normalize( vec3( deltaH * scale, 1.0 ) ), h_mid );
            #endif 
            }

            // the super fast version
            // (change number of iterations at run time)
            float intersect_cone_fixed(in vec2 dp, in vec3 ds)
            {
                // the 'not Z' component of the direction vector
                // (requires that the vector ds was normalized!)
                float iz = sqrt(1.0 - ds.z * ds.z);
                // my starting location (is at z=1,
                // and moving down so I don't have
                // to invert height maps)
                // texture lookup (and initialized to starting location)
                vec4 t;
                // scaling distance along vector ds
                float sc;
                // the ds.z component is positive!
                // (headed the wrong way, since
                // I'm using heightmaps)
                // find the initial location and height
                t = texture(u_displacement_map, dp);
                // right, I need to take one step.
                // I use the current height above the texture,
                // and the information about the cone-ratio
                // to size a single step. So it is fast and
                // precise! (like a coneified version of
                // 'space leaping', but adapted from voxels)
                sc = (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                // and repeat a few (4x) times
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                // and another five!
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                t = texture(u_displacement_map, dp + ds.xy * sc);
                sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                // return the vector length needed to hit the height-map
                return (sc);
            }

            // (and you can do LOD by changing 'conesteps' based on size/distance, etc.)
            float intersect_cone_loop(in vec2 dp, in vec3 ds)
            {
                float maxBumpHeight = u_displacement_scale;

                const int conesteps = 10; // ???
                                          // the 'not Z' component of the direction vector
                                          // (requires that the vector ds was normalized!)
                float iz = sqrt(1.0 - ds.z * ds.z);
                // my starting location (is at z=1,
                // and moving down so I don't have
                // to invert height maps)
                // texture lookup (and initialized to starting location)
                vec4 t;
                // scaling distance along vector ds
                float sc = 0.0;
                //t=texture2D(stepmap,dp);
                //return (max(0.0,-(t.b-0.5)*ds.x-(t.a-0.5)*ds.y));
                // the ds.z component is positive!
                // (headed the wrong way, since
                // I'm using heightmaps)
                // adaptive (same speed as it averages the same # steps)
                //for (int i = int(float(conesteps)*(0.5+iz)); i > 0; --i)
                // fixed
                for (int i = conesteps; i > 0; --i)
                {
                    // find the new location and height
                    t = texture(u_displacement_map, dp + ds.xy * sc);
                    t.r = t.r * maxBumpHeight;
                    t.g = t.g / maxBumpHeight;

                    // right, I need to take one step.
                    // I use the current height above the texture,
                    // and the information about the cone-ratio
                    // to size a single step. So it is fast and
                    // precise! (like a coneified version of
                    // 'space leaping', but adapted from voxels)
                    sc += (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                }
                // return the vector length needed to hit the height-map
                return (sc);
            }

            // slowest, but best quality
            float intersect_cone_exact(in vec2 dp, in vec3 ds)
            {
                vec2 texsize = textureSize(u_displacement_map, 0);

                // minimum feature size parameter
                float w = 1.0 / max(texsize.x, texsize.y);
                // the 'not Z' component of the direction vector
                // (requires that the vector ds was normalized!)
                float iz = sqrt(1.0 - ds.z * ds.z);
                // my starting location (is at z=1,
                // and moving down so I don't have
                // to invert height maps)
                // texture lookup
                vec4 t;
                // scaling distance along vector ds
                float sc = 0.0;
                // the ds.z component is positive!
                // (headed the wrong way, since
                // I'm using heightmaps)
                // find the starting location and height
                t = texture(u_displacement_map, dp);
                while (1.0 - ds.z * sc > t.r)
                {
                    // right, I need to take one step.
                    // I use the current height above the texture,
                    // and the information about the cone-ratio
                    // to size a single step. So it is fast and
                    // precise! (like a coneified version of
                    // 'space leaping', but adapted from voxels)
                    sc += w + (1.0 - ds.z * sc - t.r) / (ds.z + iz / (t.g * t.g));
                    // find the new location and height
                    t = texture(u_displacement_map, dp + ds.xy * sc);
                }
                // back off one step
                sc -= w;
                // return the vector length needed to hit the height-map
                return (sc);
            }

            // Parallax Occlusion Mapping in GLSL [http://sunandblackcat.com/tipFullView.php?topicid=28]
            vec3 ConeStep(in float frontFace, in vec3 texDir3D, in vec2 texCoord)
            {
                float maxBumpHeight = 1.0;
                vec2 quality_range = u_parallax_quality;

                // [Determinante](https://de.wikipedia.org/wiki/Determinante)
                // A x B = A.x * B.y - A.y * B.x = dot(A, vec2(B.y,-B.x)) = det(mat2(A,B))

                // [How do you detect where two line segments intersect?](https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect)
                vec2 R = normalize(vec2(length(texDir3D.xy), texDir3D.z));
                vec2 P = R * maxBumpHeight / texDir3D.z;

                vec2 tex_size = textureSize(u_displacement_map, 0).xy;
                vec2 min_tex_step = normalize(texDir3D.xy) / tex_size;
                float min_step = length(min_tex_step) * 1.0 / R.x;

                float t = 0.0;
                const int max_no_of_steps = int(5.0 + quality_range * 45.0);
                for (int i = 0; i < max_no_of_steps; ++i)
                {
                    vec3 sample_pt = vec3(texCoord.xy, maxBumpHeight) + texDir3D * t;

                    vec2 h_and_c = GetHeightAndCone(sample_pt.xy);
                    float h = h_and_c.x * maxBumpHeight;
                    float c = h_and_c.y * h_and_c.y / maxBumpHeight;

                    vec2 C = P + R * t;
                    if (C.y <= h)
                        break;

                    vec2 Q = vec2(C.x, h);
                    vec2 S = normalize(vec2(c, 1.0));
                    float new_t = dot(Q - P, vec2(S.y, -S.x)) / dot(R, vec2(S.y, -S.x));
                    t = max(t + min_step, new_t);
                }

                vec2 texC = texCoord.xy + texDir3D.xy * t;
                float mapHeight = GetHeightAndCone(texC.xy).x - step(frontFace, 0.0);
                return vec3(texC.xy, mapHeight);
            }

            void main()
            {
                vec3 objPosEs = in_data.pos;
                vec3 objNormalEs = in_data.nv;
                vec2 texCoords = in_data.uv.st;
                float frontFace = gl_FrontFacing ? 1.0 : -1.0; // TODO $$$ sign(dot(N,objPosEs));
                                                               //vec3  normalEs     = frontFace * normalize( objNormalEs );

                // orthonormal tangent space matrix
                //vec3  p_dx         = dFdx( objPosEs );
                //vec3  p_dy         = dFdy( objPosEs );
                //vec2  tc_dx        = dFdx( texCoords );
                //vec2  tc_dy        = dFdy( texCoords );
                //float texDet       = determinant( mat2( tc_dx, tc_dy ) );
                //vec3  tangentVec   = ( tc_dy.y * p_dx - tc_dx.y * p_dy ) / abs( texDet );
                //vec3  tangentEs    = normalize( tangentVec - normalEs * dot(tangentVec, normalEs ) );
                //mat3  tbnMat       = mat3( sign( texDet ) * tangentEs, cross( normalEs, tangentEs ), normalEs );

                // Followup: Normal Mapping Without Precomputed Tangents [http://www.thetenthplanet.de/archives/1180]
                vec3 N = frontFace * normalize(objNormalEs);
                vec3 dp1 = dFdx(objPosEs);
                vec3 dp2 = dFdy(objPosEs);
                vec2 duv1 = dFdx(texCoords);
                vec2 duv2 = dFdy(texCoords);
                vec3 dp2perp = cross(dp2, N);
                vec3 dp1perp = cross(N, dp1);
                vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
                vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
                float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
                mat3 tbnMat = mat3(T * invmax, B * invmax, N * u_displacement_scale);
                mat3 inv_tbnMat = inverse(tbnMat);

                vec3 texDir3D = normalize(inv_tbnMat * objPosEs);
                vec3 newTexCoords = abs(u_displacement_scale) < 0.001 ? vec3(texCoords.st, 0.0) : ConeStep(frontFace, texDir3D, texCoords.st);

                //float depth_displ    = length(tbnMat * (newTexCoords.z * texDir3D.xyz / abs(texDir3D.z))); 
                //vec3  view_pos_displ = objPosEs - depth_displ * normalize(objPosEs);
                vec3 displ_vec = tbnMat * (newTexCoords.z * texDir3D.xyz / abs(texDir3D.z));
                vec3 view_pos_displ = objPosEs - displ_vec;
                vec4 modelPos = inverse(mvp.view) * vec4(view_pos_displ, 1.0);
                vec4 clipPlane = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w);
                float clip_dist = dot(modelPos, clipPlane);
                //float clip_dist      = in_data.clip;
                if (clip_dist < 0.0)
                    discard;

                texCoords.st = newTexCoords.xy;
                vec4 normalVec = CalculateNormal(texCoords);
                //vec3  nvMappedEs   = normalize( tbnMat * normalVec.xyz );
                vec3 nvMappedEs = u_displacement_scale < 0.001 ? normalize(objNormalEs) : (normalize(transpose(inv_tbnMat) * normalVec.xyz));

                //vec3 color = in_data.col;
                vec3 color = texture(u_texture, texCoords.st).rgb;

                // ambient part
                vec3 lightCol = light_data.u_ambient * color;

                // diffuse part
                vec3 normalV = normalize(nvMappedEs);
                vec3 lightV = -normalize(mat3(mvp.view) * light_data.u_lightDir.xyz);
                float NdotL = max(0.0, dot(normalV, lightV));
                lightCol += NdotL * light_data.u_diffuse * color;

                // specular part
                vec3 eyeV = normalize(-objPosEs);
                vec3 halfV = normalize(eyeV + lightV);
                float NdotH = max(0.0, dot(normalV, halfV));
                float kSpecular = (light_data.u_shininess + 2.0) * pow(NdotH, light_data.u_shininess) / (2.0 * 3.14159265);
                lightCol += kSpecular * light_data.u_specular * color;

                fragColor = vec4(lightCol.rgb, 1.0);

                vec4 proj_pos_displ = mvp.proj * vec4(view_pos_displ.xyz, 1.0);
                float depth = 0.5 + 0.5 * proj_pos_displ.z / proj_pos_displ.w;
                gl_FragDepth = u_displacement_scale < 0.001 ? gl_FragCoord.z : depth;

                //fragColor = vec4( vec3(1.0-depth), 1.0 );
            }";

            this._parallax_prog = openGLFactory.VertexAndFragmentShaderProgram(vert_shader, frag_shader);
            this._parallax_prog.Generate();

            // Model view projection shader storage block objects and buffers
            TMVP mvp = new TMVP(Matrix4.Identity, Matrix4.Identity, Matrix4.Identity);

            this._mvp_ssbo = openGLFactory.NewStorageBuffer();
            this._mvp_ssbo.Create(ref mvp);
            this._mvp_ssbo.Bind(1);

            TLightSource light_source = new TLightSource(new Vector4(-1.0f, -0.5f, -2.0f, 0.0f), 0.2f, 0.8f, 0.8f, 10.0f);

            this._light_ssbo = openGLFactory.NewStorageBuffer();
            this._light_ssbo.Create(ref light_source);
            this._light_ssbo.Bind(2);

            // states

            GL.Viewport(0, 0, this._cx, this._cy);
            //GL.ClearColor(System.Drawing.Color.Beige);
            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            GL.Enable(EnableCap.DepthTest);

            // no face culling, because of clipping
            //GL.Enable(EnableCap.CullFace);
            GL.FrontFace(FrontFaceDirection.Ccw);
            GL.CullFace(CullFaceMode.Back);

            // matrices and controller

            this._view = Matrix4.LookAt(0.0f, 0.0f, 2.5f, 0, 0, 0, 0, 1, 0);

            float angle  = 90.0f * (float)Math.PI / 180.0f;
            float aspect = (float)this._cx / (float)this._cy;

            this._projection = Matrix4.CreatePerspectiveFieldOfView(angle, aspect, 0.1f, 100.0f);

            this._spin = new ModelSpinningControls(
                () => { return(this._period); },
                () => { return(new float[] { 0, 0, (float)this._cx, (float)this._cy }); },
                () => { return(this._view); }
                );
            this._spin.SetAttenuation(1.0f, 0.05f, 0.0f);

            // properties
            ViewModel.HeightScale  = 50;
            ViewModel.QualityScale = 50;
            ViewModel.ClipScale    = 50;
        }
        public void Setup(int cx, int cy)
        {
            this._cx = cx;
            this._cy = cy;

            // Version strings
            _version.Retrieve();

            // Get OpenGL extensions
            _extensions.Retrieve();

            // Debug callback
            _debug_callback.Init();

            // create Vertex Array Object, Array Buffer Object and Element Array Buffer Object

            (float[] attributes, uint[] indices) = new TrefoilKnot(256, 16).Create();
            TVertexFormat[] format =
            {
                new TVertexFormat(0, 0, 3, 0, false),
                new TVertexFormat(0, 1, 3, 3, false),
                //new TVertexFormat(0, ?, 2, 6, false),
                new TVertexFormat(0, 2, 4, 8, false),
            };

            _test_vao = openGLFactory.NewVertexArrayObject();
            _test_vao.AppendVertexBuffer(0, 12, attributes);
            _test_vao.Create(format, indices);
            _test_vao.Bind();

            // Create shader program

            string vert_shader = @"#version 460 core
            layout (location = 0) in vec4 a_pos;
            layout (location = 1) in vec3 a_nv;
            layout (location = 2) in vec4 a_color;
      
            layout (location = 0) out TVertexData
            {
                vec3 pos;
                vec3 nv;
                vec4 col;
            } outData;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            void main()
            {
                mat4 mv_mat     = mvp.view * mvp.model;
                mat3 normal_mat = inverse(transpose(mat3(mv_mat))); 

                outData.nv   = normalize(normal_mat * a_nv);
                outData.col  = a_color;
                vec4 viewPos = mv_mat * a_pos;
                outData.pos  = viewPos.xyz / viewPos.w;
                gl_Position  = mvp.proj * viewPos;
            }";

            string frag_shader = @"#version 460 core
            out vec4 frag_color;
            
            layout (location = 0) in TVertexData
            {
                vec3 pos;
                vec3 nv;
                vec4 col;
            } inData;

            layout(std430, binding = 2) buffer TLight
            {
                vec4  u_lightDir;
                float u_ambient;
                float u_diffuse;
                float u_specular;
                float u_shininess;
            } light_data;
      
            void main()
            {
                vec3 color = inData.col.rgb;

                // ambient part
                vec3 lightCol = light_data.u_ambient * color;
                vec3 normalV  = normalize( inData.nv );
                vec3 eyeV     = normalize( -inData.pos );
                vec3 lightV   = normalize( -light_data.u_lightDir.xyz );

                // diffuse part
                float NdotL   = max( 0.0, dot( normalV, lightV ) );
                lightCol     += NdotL * light_data.u_diffuse * color;

                // specular part
                vec3  halfV     = normalize( eyeV + lightV );
                float NdotH     = max( 0.0, dot( normalV, halfV ) );
                float kSpecular = ( light_data.u_shininess + 2.0 ) * pow( NdotH, light_data.u_shininess ) / ( 2.0 * 3.14159265 );
                lightCol       += kSpecular * light_data.u_specular * color;

                frag_color = vec4( lightCol.rgb, inData.col.a );
            }";

            this._test_prog = openGLFactory.VertexAndFragmentShaderProgram(vert_shader, frag_shader);
            this._test_prog.Generate();

            // Model view projection shader storage block objects and buffers
            TMVP mvp = new TMVP(Matrix4.Identity, Matrix4.Identity, Matrix4.Identity);

            this._mvp_ssbo = openGLFactory.NewStorageBuffer();
            this._mvp_ssbo.Create(ref mvp);
            this._mvp_ssbo.Bind(1);

            TLightSource light_source = new TLightSource(new Vector4(-1.0f, -0.5f, -2.0f, 0.0f), 0.2f, 0.8f, 0.8f, 10.0f);

            this._light_ssbo = openGLFactory.NewStorageBuffer();
            this._light_ssbo.Create(ref light_source);
            this._light_ssbo.Bind(2);

            // states

            GL.Viewport(0, 0, this._cx, this._cy);
            GL.ClearColor(System.Drawing.Color.Beige);
            //GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            GL.Enable(EnableCap.DepthTest);
            GL.Enable(EnableCap.CullFace);
            GL.FrontFace(FrontFaceDirection.Ccw);
            GL.CullFace(CullFaceMode.Back);

            // matrices and controller

            this._view = Matrix4.LookAt(0.0f, 0.0f, 1.5f, 0, 0, 0, 0, 1, 0);

            float angle  = 90.0f * (float)Math.PI / 180.0f;
            float aspect = (float)this._cx / (float)this._cy;

            this._projection = Matrix4.CreatePerspectiveFieldOfView(angle, aspect, 0.1f, 100.0f);

            var spin = new ModelSpinningControls(
                () => { return(this._period); },
                () => { return(new float[] { 0, 0, (float)this._cx, (float)this._cy }); },
                () => { return(this._view); }
                );

            spin.SetAttenuation(1.0f, 0.05f, 0.0f);
            this._controls = spin;
        }
Beispiel #4
0
        public void Setup(int cx, int cy)
        {
            this._cx = cx;
            this._cy = cy;

            // Version strings
            _version.Retrieve();

            // Get OpenGL extensions
            _extensions.Retrieve();

            // Debug callback
            _debug_callback.Init();

            // create Vertex Array Object, Array Buffer Object and Element Array Buffer Object

            (float[] attributes, uint[] indices) = new Cube().Create();
            TVertexFormat[] format =
            {
                new TVertexFormat(0, 0, 3, 0, false),
                new TVertexFormat(0, 1, 3, 3, false),
                new TVertexFormat(0, 2, 2, 6, false),
                //new TVertexFormat(0, 2, 4, 8, false),
            };

            _cube_vao = openGLFactory.NewVertexArrayObject();
            _cube_vao.AppendVertexBuffer(0, 12, attributes);
            _cube_vao.Create(format, indices);
            _cube_vao.Bind();

            // Create textures

            Assembly assembly = Assembly.GetExecutingAssembly();
            //string[] names = assembly.GetManifestResourceNames();
            Stream textue_stream          = assembly.GetManifestResourceStream("OpenTK_parallax_relief_mapping.Resource.woodtiles.jpg");
            Stream normalmap_stream       = assembly.GetManifestResourceStream("OpenTK_parallax_relief_mapping.Resource.toy_box_normal.png");
            Stream displacementmap_stream = assembly.GetManifestResourceStream("OpenTK_parallax_relief_mapping.Resource.toy_box_disp.png");

            _texture = openGLFactory.NewTexture();
            _texture.Create2D(new Bitmap(textue_stream));
            _normalmap = openGLFactory.NewTexture();
            _normalmap.Create2D(new Bitmap(normalmap_stream));
            _displacementmap = openGLFactory.NewTexture();
            _displacementmap.Create2D(new Bitmap(displacementmap_stream));

            // Create shader program

            string vert_shader = @"#version 460 core
            layout (location = 0) in vec4 a_pos;
            layout (location = 1) in vec3 a_nv;
            layout (location = 2) in vec2 a_uv;
      
            layout (location = 0) out TVertexData
            {
                vec3 w_pos;
                vec3 w_nv;
                vec2 uv;
                vec3 eye_pos;
            } outData;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            void main()
            {
                mat3 normal_mat = inverse(transpose(mat3(mvp.model))); 

                outData.w_nv    = normalize(normal_mat * a_nv);
                outData.uv      = a_uv;
                vec4 worldPos   = mvp.model * a_pos;
                outData.w_pos   = worldPos.xyz / worldPos.w;
                outData.eye_pos = inverse(mvp.view)[3].xyz;
                gl_Position     = mvp.proj * mvp.view * mvp.model * a_pos;
            }";

            string frag_shader = @"#version 460 core
            out vec4 frag_color;
            
            layout (location = 0) in TVertexData
            {
                vec3 w_pos;
                vec3 w_nv;
                vec2 uv;
                vec3 eye_pos;
            } inData;

            layout(std430, binding = 2) buffer TLight
            {
                vec4  u_lightDir;
                float u_ambient;
                float u_diffuse;
                float u_specular;
                float u_shininess;
            } light_data;

            layout(binding=1) uniform sampler2D u_diffuse;
            layout(binding=2) uniform sampler2D u_normal_map;
            layout(binding=3) uniform sampler2D u_displacement_map;
            layout(location=1) uniform float u_height_scale;

            vec2 ParallaxMapping (vec2 texCoord, vec3 viewDir)
            {
                float numLayers = 32.0 - 31.0 * abs(dot(vec3(0.0, 0.0, 1.0), viewDir));
                float layerDepth = 1.0 / numLayers;

                vec2 P = viewDir.xy / viewDir.z * u_height_scale;
                vec2 deltaTexCoords = P / numLayers;
                vec2 currentTexCoords = texCoord;

                float currentLayerDepth = 0.0;
                float currentDepthMapValue = texture2D(u_displacement_map, currentTexCoords).r;
                for (int i=0; i<32; ++ i)
                {
                    if (currentLayerDepth >= currentDepthMapValue)
                        break;
                    currentTexCoords -= deltaTexCoords;
                    currentDepthMapValue = texture2D(u_displacement_map, currentTexCoords).r;
                    currentLayerDepth += layerDepth;
                }

                vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
                float afterDepth = currentDepthMapValue - currentLayerDepth;
                float beforeDepth = texture2D(u_displacement_map, prevTexCoords).r - currentLayerDepth + layerDepth;

                float weight = afterDepth / (afterDepth - beforeDepth);
                return prevTexCoords * weight + currentTexCoords * (1.0 - weight);
            }
      
            void main()
            {
                vec3  N       = normalize(inData.w_nv);
                vec3  dp1     = dFdx( inData.w_pos );
                vec3  dp2     = dFdy( inData.w_pos );
                vec2  duv1    = dFdx( inData.uv );
                vec2  duv2    = dFdy( inData.uv );
                vec3  dp2perp = cross(dp2, N);
                vec3  dp1perp = cross(N, dp1);
                vec3  T       = dp2perp * duv1.x + dp1perp * duv2.x;
                vec3  B       = dp2perp * duv1.y + dp1perp * duv2.y;
                float invmax  = inversesqrt(max(dot(T, T), dot(B, B)));
                mat3  tm      = mat3(T * invmax, B * invmax, N);
                mat3  tbn_inv = mat3(vec3(tm[0].x, tm[1].x, tm[2].x), vec3(tm[0].y, tm[1].y, tm[2].y), vec3(tm[0].z, tm[1].z, tm[2].z));

                vec3 view_dir = tbn_inv * normalize(inData.w_pos - inData.eye_pos);
                vec2 uv = ParallaxMapping(inData.uv, view_dir);
                if (uv.x > 1.0 || uv.y > 1.0 || uv.x < 0.0 || uv.y < 0.0)
                    discard;

                vec4 color   = texture(u_diffuse, uv.xy);
                vec3 normalV = texture2D(u_normal_map, uv.st).xyz * 2.0 - 1.0;
                normalV      = normalize(vec3(normalV.xy, normalV.z / max(0.001, 10.0 * u_height_scale)));

                // ambient part
                vec3 lightCol = light_data.u_ambient * color.rgb;
                vec3 eyeV     = normalize(inData.eye_pos - inData.w_pos);
                vec3 lightV   = tbn_inv * normalize( -light_data.u_lightDir.xyz );

                // diffuse part
                float NdotL   = max( 0.0, dot( normalV, lightV ) );
                lightCol     += NdotL * light_data.u_diffuse * color.rgb;

                // specular part
                vec3  halfV     = normalize( eyeV + lightV );
                float NdotH     = max( 0.0, dot( normalV, halfV ) );
                float kSpecular = ( light_data.u_shininess + 2.0 ) * pow( NdotH, light_data.u_shininess ) / ( 2.0 * 3.14159265 );
                lightCol       += kSpecular * light_data.u_specular * color.rgb;
 
                frag_color = vec4( lightCol.rgb, color.a );
            }";

            this._parallax_prog = openGLFactory.VertexAndFragmentShaderProgram(vert_shader, frag_shader);
            this._parallax_prog.Generate();

            // Model view projection shader storage block objects and buffers
            TMVP mvp = new TMVP(Matrix4.Identity, Matrix4.Identity, Matrix4.Identity);

            this._mvp_ssbo = openGLFactory.NewStorageBuffer();
            this._mvp_ssbo.Create(ref mvp);
            this._mvp_ssbo.Bind(1);

            TLightSource light_source = new TLightSource(new Vector4(-1.0f, -0.5f, -2.0f, 0.0f), 0.2f, 0.8f, 0.8f, 10.0f);

            this._light_ssbo = openGLFactory.NewStorageBuffer();
            this._light_ssbo.Create(ref light_source);
            this._light_ssbo.Bind(2);

            // states

            GL.Viewport(0, 0, this._cx, this._cy);
            //GL.ClearColor(System.Drawing.Color.Beige);
            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            GL.Enable(EnableCap.DepthTest);
            GL.Enable(EnableCap.CullFace);
            GL.FrontFace(FrontFaceDirection.Ccw);
            GL.CullFace(CullFaceMode.Back);

            // matrices and controller

            this._view = Matrix4.LookAt(0.0f, 0.0f, 3.0f, 0, 0, 0, 0, 1, 0);

            float angle  = 90.0f * (float)Math.PI / 180.0f;
            float aspect = (float)this._cx / (float)this._cy;

            this._projection = Matrix4.CreatePerspectiveFieldOfView(angle, aspect, 0.1f, 100.0f);

            this._spin = new ModelSpinningControls(
                () => { return(this._period); },
                () => { return(new float[] { 0, 0, (float)this._cx, (float)this._cy }); },
                () => { return(this._view); }
                );
            this._spin.SetAttenuation(1.0f, 0.05f, 0.0f);

            // properties
            ViewModel.HeightScale = 100;
        }
        public void Setup(int cx, int cy)
        {
            this._cx = cx;
            this._cy = cy;

            // Version strings
            _version.Retrieve();

            // Get OpenGL extensions
            _extensions.Retrieve();

            // Debug callback
            _debug_callback.Init();

            // create Vertex Array Object, Array Buffer Object and Element Array Buffer Object

            (float[] attributes, uint[] indices) = new Cube().Create();
            TVertexFormat[] format =
            {
                new TVertexFormat(0, 0, 3, 0, false),
                new TVertexFormat(0, 1, 3, 3, false),
                new TVertexFormat(0, 2, 2, 6, false),
                //new TVertexFormat(0, 2, 4, 8, false),
            };

            _cube_vao = openGLFactory.NewVertexArrayObject();
            _cube_vao.AppendVertexBuffer(0, 12, attributes);
            _cube_vao.Create(format, indices);
            _cube_vao.Bind();

            // Create textures objects

            _tbos = new List <ITexture>();
            _tbos.Add(openGLFactory.NewTexture());
            _tbos[0].Create2D(_image_cx, _image_cy, ITexture.Format.RGBA_8);
            _tbos.Add(openGLFactory.NewTexture());
            _tbos[1].Create2D(_image_cx, _image_cy, ITexture.Format.RGBA_F32);
            _tbos.Add(openGLFactory.NewTexture());

            _tbos[2].Create2D(_image_cx, _image_cy, ITexture.Format.RGBA_F32);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);

            // Create generators

            this._generators = new List <TextureGenerator>();
            this._generators.Add(new TextureGenerator(openGLFactory, TextureGenerator.TType.texture_test1, new ITexture[] { _tbos[0] }));
            this._generators.Add(new TextureGenerator(openGLFactory, TextureGenerator.TType.heightmap_test1, new ITexture[] { _tbos[1] }));
            this._generators.Add(new TextureGenerator(openGLFactory, TextureGenerator.TType.cone_step_map, new ITexture[] { _tbos[2] }, new ITexture[] { _tbos[1] }));

            // Create textures

            foreach (var generator in this._generators)
            {
                generator.Generate();
            }

            // Create shader program

            string vert_shader = @"#version 460 core
            
            layout (location = 0) in vec3 inPos;
            layout (location = 1) in vec3 inNV;
            layout (location = 2) in vec2 inUV;

            out TVertexData
            {
                vec3 world_pos;
                vec3 world_nv;
                vec2 uv;
            } out_data;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            void main()
            {
                vec4 worldPos      = mvp.model * vec4(inPos, 1.0);
                out_data.world_pos = worldPos.xyz / worldPos.w;
                out_data.world_nv  = normalize( mat3(mvp.model) * inNV );
                out_data.uv        = inUV;
            }";

            string geo_shader = @"#version 460 core
            
            layout( triangles ) in;
            layout( triangle_strip, max_vertices = 15 ) out;

            in TVertexData
            {
                vec3 world_pos;
                vec3 world_nv;
                vec2 uv;
            } inData[];

            out TGeometryData
            {
                vec3  pos;
                vec3  nv;
                vec3  tv;
                vec3  bv;
                vec3  uvh;
                vec4  d;
                float clip;
            } outData;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            layout(location=1) uniform vec4  u_clipPlane;
            layout(location=2) uniform float u_displacement_scale;

            void main()
            {
                // tangent space
                //vec3  p_dA       = vsPos[1].xyz - vsPos[0].xyz;
                //vec3  p_dB       = vsPos[2].xyz - vsPos[0].xyz;
                //vec2  tc_dA      = inData[1].uv - inData[0].uv;
                //vec2  tc_dB      = inData[2].uv - inData[0].uv;
                //float texDet     = determinant( mat2( tc_dA, tc_dB ) );
                //outData.vsTV     = ( tc_dB.y * p_dA - tc_dA.y * p_dB ) / texDet;
                //outData.vsBVsign = sign(texDet);

                vec3 world_pos_up[3];
                for (int i = 0; i < 3; ++ i)
                    world_pos_up[i] = inData[i].world_pos + inData[i].world_nv * u_displacement_scale;

                vec3 view_nv[3];
                vec3 view_pos[3];
                vec3 view_pos_up[3];
                for (int i = 0; i < 3; ++ i)
                {
                    vec4 viewPos   = mvp.view * vec4(inData[i].world_pos, 1.0);
                    view_nv[i]     = normalize(mat3(mvp.view) * inData[i].world_nv);
                    view_pos[i]    = viewPos.xyz;
                    view_pos_up[i] = view_pos[i] + view_nv[i] * u_displacement_scale;
                    //view_pos_up[i] = (mvp.view * vec4(world_pos_up[i], 1.0)).xyz;
                }

                // tangent space
                // Followup: Normal Mapping Without Precomputed Tangents [http://www.thetenthplanet.de/archives/1180]
                vec3  dp1  = view_pos[1].xyz - view_pos[0].xyz;
                vec3  dp2  = view_pos[2].xyz - view_pos[0].xyz;
                vec2  duv1 = inData[1].uv.xy - inData[0].uv.xy;
                vec2  duv2 = inData[2].uv.xy - inData[0].uv.xy;

                vec3 nv[3];
                vec3 tv[3];
                vec3 bv[3];
                for ( int i=0; i < 3; ++i )
                {
                    vec3 dp2perp = cross(dp2, view_nv[i]); 
                    vec3 dp1perp = cross(view_nv[i], dp1);
        
                    nv[i] = view_nv[i] * u_displacement_scale;
                    tv[i] = dp2perp * duv1.x + dp1perp * duv2.x;
                    bv[i] = dp2perp * duv1.y + dp1perp * duv2.y;
                }

    
                // distance to opposite planes
                float d[3];
                float d_up[3];
                float d_opp[3];
                float d_opp_up[3];
                float d_top[3];
                for ( int i0=0; i0 < 3; ++i0 )
                {
                  d[i0]    = length(view_pos[i0].xyz);
                  d_up[i0] = length(view_pos_up[i0].xyz);

                  int i1 = (i0+1) % 3; 
                  int i2 = (i0+2) % 3; 
                  vec3 edge    = view_pos[i2].xyz - view_pos[i1].xyz;
                  vec3 edge_up = view_pos_up[i2].xyz - view_pos_up[i1].xyz;
                  vec3 up      = view_nv[i1].xyz + view_nv[i2].xyz;

                  // intersect the view ray trough a corner point of the prism (with triangular base)
                  // with the opposite side face of the prism
                  //
                  // d = dot(P0 - R0, N) / dot(D, N)
                  //
                  // R0 : point on the ray
                  // D  : direction of the ray
                  // P0 : point on the plane
                  // N  : norma vector of the plane
                  // d  :: distance from R0 to the intersection with the plane along D
      
                  //vec3  R0      = vec3(view_pos[i0].xy, 0.0); // for orthographic projection
                  //vec3  D       = vec3(0.0, 0.0, -1.0); // for orthographic projection
                  vec3  R0      = vec3(0.0); // for persepctive projection
                  vec3  D       = normalize(view_pos[i0].xyz); // for persepctive projection
                  vec3  N       = normalize(cross(edge, up));
                  vec3  P0      = (view_pos[i1].xyz+view_pos[i2].xyz)/2.0;
                  d_opp[i0]     = dot(P0 - R0, N) / dot(D, N);

                  //vec3  R0_up   = vec2(view_pos_up[i0].xyz, 0.0); // for orthographic projection
                  //vec3  D_up  = vec3(0.0, 0.0, -1.0); // for orthographic projection
                  vec3  R0_up   = vec3(0.0); // for persepctive projection 
                  vec3  D_up    = normalize(view_pos_up[i0].xyz); // for persepctive projection
                  vec3  N_up    = normalize(cross(edge_up, up));
                  vec3  P0_up   = (view_pos_up[i1].xyz+view_pos_up[i2].xyz)/2.0;
                  d_opp_up[i0]  = dot(P0_up - R0_up, N_up) / dot(D_up, N_up);

                  //vec3  N_top   = view_nv[i0];
                  vec3  N_top   = normalize(view_nv[0]+view_nv[1]+view_nv[2]);
                  vec3  P0_top  = (view_pos_up[0].xyz + view_pos_up[1].xyz + view_pos_up[2].xyz)/3.0;
                  d_top[i0]     = dot(P0_top - R0, N_top) / dot(D, N_top);
                }

                vec4 clipPlane = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w);

                for ( int i=0; i < 3; ++i )
                {
                    outData.nv   = nv[i];
                    outData.tv   = tv[i];
                    outData.bv   = bv[i];
                    outData.pos  = view_pos[i];
                    outData.uvh  = vec3(inData[i].uv, 0.0);
                    outData.d    = vec4( i==0 ? d_opp[i] : d[i], i==1 ? d_opp[i] : d[i], i==2 ? d_opp[i] : d[i], d_top[i] );
                    outData.clip = dot(vec4(inData[i].world_pos, 1.0), clipPlane);
                    gl_Position  = mvp.proj * vec4( outData.pos, 1.0 );
                    EmitVertex();
                }
                EndPrimitive();

                vec3 cpt_tri = (view_pos[0] + view_pos[1] + view_pos[2]) / 3.0;
                for ( int i0=0; i0 < 3; ++i0 )
                {
                    int i1 = (i0+1) % 3;
                    int i2 = (i0+2) % 3; 

                    vec3 cpt_edge    = (view_pos[i0] + view_pos[i1]) / 2.0;
                    vec3 dir_to_edge = cpt_edge - cpt_tri; // direction from thge center of the triangle to the edge

                    vec3 edge    = view_pos[i1] - view_pos[i0];
                    vec3 nv_edge  = nv[i0] + nv[i1];
                    vec3 nv_side = cross(edge, nv_edge); // normal vector of a side of the prism
                    nv_side *= sign(dot(nv_side, dir_to_edge)); // orentate the normal vector out of the center of the triangle

                    // a front face is a side of the prism, where the normal vector is directed against the view vector
                    float frontface = sign(dot(cpt_edge, -nv_side));

                    float d_opp0, d_opp1, d_opp_up0, d_opp_up1;
                    if ( frontface > 0.0 )
                    {
                        d_opp0    = max(d[i0], d_opp[i0]);
                        d_opp1    = max(d[i1], d_opp[i1]);
                        d_opp_up0 = max(d_up[i0], d_opp_up[i0]);
                        d_opp_up1 = max(d_up[i1], d_opp_up[i1]);
                    }
                    else
                    {
                        d_opp0    = min(d[i0], d_opp[i0]);
                        d_opp1    = min(d[i1], d_opp[i1]);
                        d_opp_up0 = min(d_up[i0], d_opp_up[i0]);
                        d_opp_up1 = min(d_up[i1], d_opp_up[i1]);
                    }

                    outData.nv   = nv[i0];
                    outData.tv   = tv[i0];
                    outData.bv   = bv[i0];
                    outData.pos  = view_pos[i0];
                    outData.uvh  = vec3(inData[i0].uv, 0.0);
                    outData.d    = vec4(d_opp0, d[i0], frontface, d_top[i0]);
                    outData.clip = dot(vec4(inData[i0].world_pos, 1.0), clipPlane);
                    gl_Position  = mvp.proj * vec4( outData.pos, 1.0 );
                    EmitVertex();

                    outData.nv   = nv[i1];
                    outData.tv   = tv[i1];
                    outData.bv   = bv[i1];
                    outData.pos  = view_pos[i1];
                    outData.uvh  = vec3(inData[i1].uv, 0.0);
                    outData.d    = vec4(d[i1], d_opp1, frontface, d_top[i1]);
                    outData.clip = dot(vec4(inData[i1].world_pos, 1.0), clipPlane);
                    gl_Position  = mvp.proj * vec4( outData.pos, 1.0 );
                    EmitVertex();

                    outData.nv   = nv[i0];
                    outData.tv   = tv[i0];
                    outData.bv   = bv[i0];
                    outData.pos  = view_pos_up[i0];
                    outData.uvh  = vec3(inData[i0].uv, 1.0);
                    outData.d    = vec4(d_opp_up0, d_up[i0], frontface, 0.0);
                    outData.clip = dot(vec4(world_pos_up[i0], 1.0), clipPlane);
                    gl_Position  = mvp.proj * vec4( outData.pos, 1.0 );
                    EmitVertex();

                    outData.nv   = nv[i1];
                    outData.tv   = tv[i1];
                    outData.bv   = bv[i1];
                    outData.pos  = view_pos_up[i1];
                    outData.uvh  = vec3(inData[i1].uv, 1.0);
                    outData.d    = vec4(d_up[i1], d_opp_up1, frontface, 0.0);
                    outData.clip = dot(vec4(world_pos_up[i1], 1.0), clipPlane);
                    gl_Position  = mvp.proj * vec4( outData.pos, 1.0 );
                    EmitVertex();

                    EndPrimitive();
                }
            }";

            string frag_shader = @"#version 460 core
            //#define NORMAL_MAP_TEXTURE
            #define NORMAL_MAP_QUALITY 1

            in TGeometryData
            {
                vec3  pos;
                vec3  nv;
                vec3  tv;
                vec3  bv;
                vec3  uvh;
                vec4  d;
                float clip;
            } in_data;

            out vec4 fragColor;

            layout(std430, binding = 2) buffer TLight
            {
                vec4  u_lightDir;
                float u_ambient;
                float u_diffuse;
                float u_specular;
                float u_shininess;
            } light_data;

            layout(binding=1)  uniform sampler2D u_texture;
            layout(binding=2)  uniform sampler2D u_displacement_map;
            layout(location=2) uniform float     u_displacement_scale;
            layout(location=3) uniform vec2      u_parallax_quality;

            layout(std430, binding = 1) buffer MVP
            {
                mat4 proj;
                mat4 view;
                mat4 model;
            } mvp;

            layout(location=1) uniform vec4 u_clipPlane;

            #if defined(NORMAL_MAP_TEXTURE)
            uniform sampler2D u_normal_map;
            #endif

            float CalculateHeight( in vec2 texCoords )
            {
                float height = texture( u_displacement_map, texCoords ).x;
                return clamp( height, 0.0, 1.0 );
            }

            vec2 GetHeightAndCone( in vec2 texCoords )
            {
                vec2 h_and_c = texture( u_displacement_map, texCoords ).rg;
                return clamp( h_and_c, 0.0, 1.0 );
            }

            vec4 CalculateNormal( in vec2 texCoords )
            {
            #if defined(NORMAL_MAP_TEXTURE)
                float height = CalculateHeight( texCoords );
                vec3  tempNV = texture( u_normal_map, texCoords ).xyz * 2.0 / 1.0;
                return vec4( normalize( tempNV ), height );
            #else
                vec2 texOffs = 1.0 / vec2(textureSize( u_displacement_map, 0 ).xy);
                vec2 scale   = 1.0 / texOffs;
            #if NORMAL_MAP_QUALITY > 1
                float hx[9];
                hx[0] = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0, -1.0) ).r;
                hx[1] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r;
                hx[2] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, -1.0) ).r;
                hx[3] = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0,  0.0) ).r;
                hx[4] = texture( u_displacement_map, texCoords.st ).r;
                hx[5] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, 0.0) ).r;
                hx[6] = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0, 1.0) ).r;
                hx[7] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, 1.0) ).r;
                hx[8] = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, 1.0) ).r;
                vec2  deltaH = vec2(hx[0]-hx[2] + 2.0*(hx[3]-hx[5]) + hx[6]-hx[8], hx[0]-hx[6] + 2.0*(hx[1]-hx[7]) + hx[2]-hx[8]); 
                float h_mid  = hx[4];
            #elif NORMAL_MAP_QUALITY > 0
                float h_mid  = texture( u_displacement_map, texCoords.st ).r;
                float h_xa   = texture( u_displacement_map, texCoords.st + texOffs * vec2(-1.0,  0.0) ).r;
                float h_xb   = texture( u_displacement_map, texCoords.st + texOffs * vec2( 1.0,  0.0) ).r;
                float h_ya   = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r;
                float h_yb   = texture( u_displacement_map, texCoords.st + texOffs * vec2( 0.0,  1.0) ).r;
                vec2  deltaH = vec2(h_xa-h_xb, h_ya-h_yb); 
            #else
                vec4  heights = textureGather( u_displacement_map, texCoords, 0 );
                vec2  deltaH  = vec2(dot(heights, vec4(1.0, -1.0, -1.0, 1.0)), dot(heights, vec4(-1.0, -1.0, 1.0, 1.0)));
                float h_mid   = heights.w; 
            #endif
                return vec4( normalize( vec3( deltaH * scale, 1.0 ) ), h_mid );
            #endif 
            }

            vec3 Parallax( in float frontFace, in vec3 texCoord, in vec3 tbnP0, in vec3 tbnP1, in vec3 tbnStep )
            {   
                // inverse height map: -1 for inverse height map or 1 if not inverse
                // height maps of back faces base triangles are inverted
                float back_face = step(0.0, -frontFace); 
                vec3 texC0 = texCoord.xyz + tbnP0 + back_face * vec3(tbnStep.xy, 0.0);
                vec3 texC1 = texCoord.xyz + tbnP1 + back_face * vec3(tbnStep.xy, 0.0);

                // sample steps and quality
                vec2  quality_range  = u_parallax_quality;
                float quality        = mix( quality_range.x, quality_range.y, 1.0 - abs(normalize(tbnStep).z) );
                float numSteps       = clamp( quality * 50.0, 1.0, 50.0 );
                int   numBinarySteps = int( clamp( quality * 10.0, 1.0, 10.0 ) );

                // change of the height per step
                float bumpHeightStep = (texC0.z-texC1.z) / (numSteps-1.0);

                float bestBumpHeight = texC1.z;
                float mapHeight      = 1.0;
                for ( int i = 0; i < int( numSteps ); ++ i )
                {
                    mapHeight = back_face + frontFace * CalculateHeight( mix(texC0.xy, texC1.xy, (bestBumpHeight-texC0.z)/(texC1.z-texC0.z)) );
                    if ( mapHeight >= bestBumpHeight || bestBumpHeight > 1.0 )
                        break;
                    bestBumpHeight += bumpHeightStep;   
                } 

                if ( texCoord.z < 0.0001 || bestBumpHeight >= 0.0 ) // if not a silhouett 
                {
                    // binary steps, starting at the previous sample point 
                    bestBumpHeight -= bumpHeightStep;
                    for ( int i = 0; i < numBinarySteps; ++ i )
                    {
                        bumpHeightStep *= 0.5;
                        bestBumpHeight += bumpHeightStep;
                        mapHeight       = back_face + frontFace * CalculateHeight( mix(texC0.xy, texC1.xy, (bestBumpHeight-texC0.z)/(texC1.z-texC0.z)) );
                        bestBumpHeight -= ( bestBumpHeight < mapHeight ) ? bumpHeightStep : 0.0;
                    }

                    // final linear interpolation between the last to heights 
                    bestBumpHeight += bumpHeightStep * clamp( ( bestBumpHeight - mapHeight ) / abs(bumpHeightStep), 0.0, 1.0 );
                }

                // set displaced texture coordiante and intersection height
                vec2 texC  = mix(texC0.xy, texC1.xy, (bestBumpHeight-texC0.z)/(texC1.z-texC0.z));
                mapHeight  = bestBumpHeight;
    
                return vec3(texC.xy, mapHeight);
            }

            void main()
            {
                vec3  objPosEs    = in_data.pos;
                vec3  objNormalEs = in_data.nv;
                vec3  texCoords   = in_data.uvh.stp;
                float frontFace   = (texCoords.p > 0.0) ? 1.0 : (gl_FrontFacing ? 1.0 : -1.0); // TODO $$$ sign(dot(N,objPosEs));
    
                //vec3  tangentEs    = normalize( tangentVec - normalEs * dot(tangentVec, normalEs ) );
                //mat3  tbnMat       = mat3( tangentEs, binormalSign * cross( normalEs, tangentEs ), normalEs );

                // tangent space
                // Followup: Normal Mapping Without Precomputed Tangents [http://www.thetenthplanet.de/archives/1180]
                //   If backface, then the normal vector is downwards the (co-)tangent space.
                //   In this case the normal has to be mirrored to make the parallax algorithm prpper work.
                vec3  N           = frontFace * objNormalEs;  
                vec3  T           = in_data.tv;
                vec3  B           = in_data.bv;
                float invmax      = inversesqrt(max(dot(T, T), dot(B, B)));
                mat3  tbnMat      = mat3(T * invmax, B * invmax, N * invmax);
                mat3  inv_tbnMat  = inverse( tbnMat );

                // distances to the sides of the prism
                bool  is_silhouette    = texCoords.p > 0.0001;
                bool  silhouette_front = in_data.d.z > 0.0;
                float df = length( objPosEs );
                float d0;
                float d1;
                if ( is_silhouette == false )
                {
                    if ( frontFace > 0.0 )
                    {
                        d1 = 0.0;
                        d0 = min(min(in_data.d.x, in_data.d.y), in_data.d.z) - df; // TODO $$$ * 0.9
                    }
                    else
                    {
                        d0 = 0.0;
                        d1 = max(max(in_data.d.x, in_data.d.y), in_data.d.z) - df;
                    }
                }
                else
                {
                    d1 = min(in_data.d.x, in_data.d.y) - df;
                    d0 = max(in_data.d.x, in_data.d.y) - df;
                }

                // intersection points
                vec3  V  = objPosEs / df;
                vec3  P0 = V * d0;
                vec3  P1 = V * d1;
   
                vec3  tbnP0        = inv_tbnMat * P0;
                vec3  tbnP1        = inv_tbnMat * P1;
                vec3  tbnDir       = normalize(inv_tbnMat * objPosEs);
                vec3  tbnTopMax    = tbnDir / tbnDir.z;

                // geometry situation
                float base_height  = texCoords.p;                     // intersection level (height) on the silhouette (side of prism geometry)
                bool  is_up_isect  = is_silhouette && tbnDir.z > 0.0; // upwards intersection on potential silhouette (side of prism geometry)

                // sample start and end height (level)
                float delta_height0 = is_up_isect ? 1.05*(1.0-base_height) : base_height; // TODO $$$ 1.05 ??? 
                float delta_height1 = is_up_isect ? 0.0 : (base_height - 1.0);

                // sample distance
                //vec3 texDist = tbnDir / abs(tbnDir.z); // (z is negative) the direction vector points downwards int tangent-space
                vec3 texDist = is_silhouette == false ? tbnDir / abs(tbnDir.z) : tbnDir / max(abs(tbnDir.z), 0.5*length(tbnDir.xy));
                vec3 tbnStep = vec3(texDist.xy, sign(tbnDir.z));

                // start and end of samples
                tbnP0 = delta_height0 * tbnStep; // sample end - bottom of prism 
                tbnP1 = delta_height1 * tbnStep; // sample start - top of prism 
                if ( is_silhouette )
                {
                    if ( silhouette_front )
                    {
                        tbnP1 = vec3(0.0);
                    }
                    else
                    {
                        tbnP0 = vec3(0.0);
                    }
                }

                vec3  newTexCoords = abs(u_displacement_scale) < 0.001 ? vec3(texCoords.st, 0.0) : Parallax( frontFace, texCoords.stp, tbnP0, tbnP1, tbnStep );
                vec3  tex_offst    = newTexCoords.stp-texCoords.stp;
    
                // slihouett discard (clipping)
                if ( is_silhouette )
                {
                    if ( newTexCoords.z > 1.000001 ||                // clip at top plane of the prism
                         newTexCoords.z < 0.0 ||                    // clip at bottom plane of the prism
                         dot(tex_offst, tbnDir)*in_data.d.z < 0.0 ) // clip back side faces at the back and clip front side faces at the front
                        discard;
                    if ( silhouette_front == false && is_up_isect )
                        discard;
                }
    
                vec3  displ_vec      = tbnMat * tex_offst/invmax;
                vec3  view_pos_displ = objPosEs + displ_vec;
                texCoords.st         = newTexCoords.xy;

            #define DEBUG_CLIP
            #define DEBUG_CLIP_DISPLACED

            #if defined (DEBUG_CLIP)
                vec4  modelPos       = inverse(mvp.view) * vec4(view_pos_displ, 1.0);
                vec4  clipPlane      = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w);
            #if defined (DEBUG_CLIP_DISPLACED)
                float clip_dist      = dot(modelPos, clipPlane);
            #else
                float clip_dist      = in_data.clip;
            #endif
                if ( clip_dist < 0.0 )
                    discard;
            #endif
    
                vec4  normalVec = CalculateNormal( texCoords.st );
                // If back face, then the height map has been inverted (except cone step map). This causes that the normalvector has to be adapted.
                normalVec.xy *= frontFace;
                //vec3  nvMappedEs = normalize( tbnMat * normalVec.xyz );
                vec3  nvMappedEs = normalize( transpose(inv_tbnMat) * normalVec.xyz ); // TODO $$$ evaluate `invmax`?

                vec3 color = texture( u_texture, texCoords.st ).rgb;

                // ambient part
                vec3 lightCol = light_data.u_ambient * color;

                // diffuse part
                vec3  normalV = normalize( nvMappedEs );
                vec3  lightV  = -normalize(mat3(mvp.view) * light_data.u_lightDir.xyz);
                float NdotL   = max( 0.0, dot( normalV, lightV ) );
                lightCol     += NdotL * light_data.u_diffuse * color;
    
                // specular part
                vec3  eyeV      = normalize( -objPosEs );
                vec3  halfV     = normalize( eyeV + lightV );
                float NdotH     = max( 0.0, dot( normalV, halfV ) );
                float kSpecular = ( light_data.u_shininess + 2.0 ) * pow( NdotH, light_data.u_shininess ) / ( 2.0 * 3.14159265 );
                lightCol       += kSpecular * light_data.u_specular * color;

                fragColor = vec4( lightCol.rgb, 1.0 );

                vec4 proj_pos_displ = mvp.proj * vec4(view_pos_displ.xyz, 1.0);
                float depth = 0.5 + 0.5 * proj_pos_displ.z / proj_pos_displ.w;

                gl_FragDepth = depth;

            //#define DEBUG_FRONT_SILHOUETTES
            //#define DEBUG_BACK_SILHOUETTES
            //#define DEBUG_DEPTH

            #if defined(DEBUG_FRONT_SILHOUETTES)
                if ( texCoords.p < 0.0001 )
                    discard;
                if ( in_data.d.z < 0.0 )
                    discard;
                fragColor = vec4(vec2(in_data.d.xy-df), in_data.d.z, 1.0);
                //fragColor = vec4(vec2(d1), in_data.d.z, 1.0);
            #endif

            #if defined(DEBUG_BACK_SILHOUETTES)
                if ( texCoords.p < 0.0001 )
                    discard;
                if ( in_data.d.z > 0.0 )
                    discard;
                fragColor = vec4(vec2(df-in_data.d.xy), -in_data.d.z, 1.0);
                //fragColor = vec4(vec2(-d0), -in_data.d.z, 1.0);
            #endif

            #if defined(DEBUG_DEPTH)
                fragColor = vec4( vec3(1.0-depth), 1.0 );
            #endif
            }";

            this._parallax_prog = openGLFactory.VertexGeometryFragmentShaderProgram(vert_shader, geo_shader, frag_shader);
            this._parallax_prog.Generate();

            // Model view projection shader storage block objects and buffers
            TMVP mvp = new TMVP(Matrix4.Identity, Matrix4.Identity, Matrix4.Identity);

            this._mvp_ssbo = openGLFactory.NewStorageBuffer();
            this._mvp_ssbo.Create(ref mvp);
            this._mvp_ssbo.Bind(1);

            TLightSource light_source = new TLightSource(new Vector4(-1.0f, -0.5f, -2.0f, 0.0f), 0.2f, 0.8f, 0.8f, 10.0f);

            this._light_ssbo = openGLFactory.NewStorageBuffer();
            this._light_ssbo.Create(ref light_source);
            this._light_ssbo.Bind(2);

            // states

            GL.Viewport(0, 0, this._cx, this._cy);
            //GL.ClearColor(System.Drawing.Color.Beige);
            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
            GL.Enable(EnableCap.DepthTest);

            // no face culling, because of clipping
            //GL.Enable(EnableCap.CullFace);
            GL.FrontFace(FrontFaceDirection.Ccw);
            GL.CullFace(CullFaceMode.Back);

            // matrices and controller

            this._view = Matrix4.LookAt(0.0f, 0.0f, 2.5f, 0, 0, 0, 0, 1, 0);

            float angle  = 90.0f * (float)Math.PI / 180.0f;
            float aspect = (float)this._cx / (float)this._cy;

            this._projection = Matrix4.CreatePerspectiveFieldOfView(angle, aspect, 0.1f, 100.0f);

            this._spin = new ModelSpinningControls(
                () => { return(this._period); },
                () => { return(new float[] { 0, 0, (float)this._cx, (float)this._cy }); },
                () => { return(this._view); }
                );
            this._spin.SetAttenuation(1.0f, 0.05f, 0.0f);

            // properties
            ViewModel.HeightScale  = 50;
            ViewModel.QualityScale = 50;
            ViewModel.ClipScale    = 50;
        }