OpenGL - Geometry shader shadow mapping pass performing terribly - c#
I'm calculating shadows for a number of point lights using Variance Shadow Mapping. All 6 faces of the cubemap are rendered in a single pass with a geometry shader, this repeats for each light source, and the whole lot is stored in a cubemap array. This all runs fine, 16 lights at 60fps no problem.
Chasing further optimisation, I tried to move the entire process to a single geometry shader pass, only to hit the only 113 vertex output limit of my hardware. Out of curiosity I decided to render 4 lights only (72 emitted vertices) and to my surprise it dropped to 24fps.
So why is it that 16 lights with 16 render passes perform significantly better than 4 lights in a single pass?
The code is essentially identical.
#version 400 core
layout(triangles) in;
layout (triangle_strip, max_vertices=18) out;
uniform int lightID;
out vec4 frag_position;
uniform mat4 projectionMatrix;
uniform mat4 shadowTransforms[6];
void main()
{
for(int face = 0; face < 6; face++)
{
gl_Layer = face + (lightID * 6);
for(int i=0; i<3; i++)
{
frag_position = shadowTransforms[face] * gl_in[i].gl_Position;
gl_Position = projectionMatrix * shadowTransforms[face] * gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}
}
versus
#version 400 core
layout(triangles) in;
layout (triangle_strip, max_vertices=72) out;
out vec4 frag_position;
uniform mat4 projectionMatrix;
uniform mat4 shadowTransforms[24];
void main()
{
for (int lightSource = 0; lightSource < 4; lightSource++)
{
for(int face = 0; face < 6; face++)
{
gl_Layer = face + (lightSource * 6);
for(int i=0; i<3; i++)
{
frag_position = shadowTransforms[gl_Layer] * gl_in[i].gl_Position;
gl_Position = projectionMatrix * shadowTransforms[gl_Layer] * gl_in[i].gl_Position;
EmitVertex();
}
EndPrimitive();
}
}
}
And
public void ShadowMapsPass(Shader shader)
{
// Setup
GL.UseProgram(shader.ID);
GL.Viewport(0, 0, CubeMapArray.size, CubeMapArray.size);
// Clear the cubemarray array data from the previous frame
GL.BindFramebuffer(FramebufferTarget.Framebuffer, shadowMapArray.FBO_handle);
GL.ClearColor(Color.White);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
for (int j = 0; j < lights.Count; j++)
{
// Create the light's view matrices
List<Matrix4> shadowTransforms = new List<Matrix4>();
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(1, 0, 0), new Vector3(0, -1, 0)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(-1, 0, 0), new Vector3(0, -1, 0)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 1, 0), new Vector3(0, 0, 1)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, -1, 0), new Vector3(0, 0, -1)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, 1), new Vector3(0, -1, 0)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, -1), new Vector3(0, -1, 0)));
// Send uniforms to the shader
for (int i = 0; i < 6; i++)
{
Matrix4 shadowTransform = shadowTransforms[i];
GL.UniformMatrix4(shader.getUniformID("shadowTransforms[" + i + "]"), false, ref shadowTransform);
}
GL.Uniform1(shader.getUniformID("lightID"), j);
DrawScene(shader, false);
}
}
versus
public void ShadowMapsPass(Shader shader)
{
// Setup
GL.UseProgram(shader.ID);
GL.Viewport(0, 0, CubeMapArray.size, CubeMapArray.size);
// Clear the cubemarray array data from the previous frame
GL.BindFramebuffer(FramebufferTarget.Framebuffer, shadowMapArray.FBO_handle);
GL.ClearColor(Color.White);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// Create the light's view matrices
List<Matrix4> shadowTransforms = new List<Matrix4>();
for (int j = 0; j < lights.Count; j++)
{
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(1, 0, 0), new Vector3(0, -1, 0)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(-1, 0, 0), new Vector3(0, -1, 0)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 1, 0), new Vector3(0, 0, 1)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, -1, 0), new Vector3(0, 0, -1)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, 1), new Vector3(0, -1, 0)));
shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, -1), new Vector3(0, -1, 0)));
}
// Send uniforms to the shader
for (int i = 0; i < shadowTransforms.Count; i++)
{
Matrix4 shadowTransform = shadowTransforms[i];
GL.UniformMatrix4(shader.getUniformID("shadowTransforms[" + i + "]"), false, ref shadowTransform);
}
DrawScene(shader, false);
}
I'd guess fewer opportunities for parallel code execution in the second form. The first version of the geometry shader generates 18 vertices and must be executed 4 times, but those 4 executions can run in parallel. The second version generates 72 vertices one after the other.
Related
Sprite Sheet Animation in OpenTK
I have been trying to make my first ever game engine (in OpenTK and Im stuck. I am trying to incoperate animations and I'm trying to use sprite sheets because they would greatly decrease filesize. I know that I need to use a for loop to draw all the sprites in sequence, but I dont have a clue about what I should do to make it work with spritesheets. Here is my current drawing code: //The Basis fuction for all drawing done in the application public static void Draw (Texture2D texture, Vector2 position, Vector2 scale, Color color, Vector2 origin, RectangleF? SourceRec = null) { Vector2[] vertices = new Vector2[4] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), }; GL.BindTexture(TextureTarget.Texture2D, texture.ID); //Beginning to draw on the screen GL.Begin(PrimitiveType.Quads); GL.Color3(color); for (int i = 0; i < 4; i++) { GL.TexCoord2((SourceRec.Value.Left + vertices[i].X * SourceRec.Value.Width) / texture.Width, (SourceRec.Value.Left + vertices[i].Y * SourceRec.Value.Height) / texture.Height); vertices[i].X *= texture.Width; vertices[i].Y *= texture.Height; vertices[i] -= origin; vertices[i] *= scale; vertices[i] += position; GL.Vertex2(vertices[i]); } GL.End(); } public static void Begin(int screenWidth, int screenHeight) { GL.MatrixMode(MatrixMode.Projection); GL.LoadIdentity(); GL.Ortho(-screenWidth / 2, screenWidth / 2, screenHeight / 2, -screenHeight / 2, 0f, 1f); } Thanks in advance!!! :)
Firstly, I beg you, abandon the fixed function pipeline. It blinds you and restricts your understanding and creativity. Move to core 3.1+, it's structural perfection is matched only by it's potentiality, and the collaborative forethought and innovation will truly move you. Now, you'll want to store your sprite sheet images as a Texture2DArray. It took me some time to figure out how to load these correctly: public SpriteSheet(string filename, int spriteWidth, int spriteHeight) { // Assign ID and get name this.textureID = GL.GenTexture(); this.spriteSheetName = Path.GetFileNameWithoutExtension(filename); // Bind the Texture Array and set appropriate parameters GL.BindTexture(TextureTarget.Texture2DArray, textureID); GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); // Load the image file Bitmap image = new Bitmap(#"Tiles/" + filename); BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); // Determine columns and rows int spriteSheetwidth = image.Width; int spriteSheetheight = image.Height; int columns = spriteSheetwidth / spriteWidth; int rows = spriteSheetheight / spriteHeight; // Allocate storage GL.TexStorage3D(TextureTarget3d.Texture2DArray, 1, SizedInternalFormat.Rgba8, spriteWidth, spriteHeight, rows * columns); // Split the loaded image into individual Texture2D slices GL.PixelStore(PixelStoreParameter.UnpackRowLength, spriteSheetwidth); for (int i = 0; i < columns * rows; i++) { GL.TexSubImage3D(TextureTarget.Texture2DArray, 0, 0, 0, i, spriteWidth, spriteHeight, 1, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0 + (spriteWidth * 4 * (i % columns)) + (spriteSheetwidth * 4 * spriteHeight * (i / columns))); // 4 bytes in an Bgra value. } image.UnlockBits(data); } Then you can simply draw a quad, and the Z texture co-ordinate is the Texture2D index in the Texture2DArray. Note frag_texcoord is of type vec3. #version 330 core in vec3 frag_texcoord; out vec4 outColor; uniform sampler2DArray tex; void main() { outColor = texture(tex, vec3(frag_texcoord)); }
Texturing triangles
I am trying to texture triangle with OpenTK ES with this code using System; using OpenTK; using OpenTK.Graphics.ES11; using OpenTK.Platform.Android; using Android.Content; namespace TexturedTriangle { class GLView1 AndroidGameView { public GLView1(Context context) base(context) { } protected override void CreateFrameBuffer() { base.CreateFrameBuffer(); } Vector2[] TexCoords = { new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), }; Vector2[] Vertices = { new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 1f), }; int[] Tex = new int[1]; int[] VBOs = new int[2]; protected override void OnLoad(EventArgs e) { base.OnLoad(e); GL.ClearColor(0, 0, 0, 1f); GL.Clear((int)All.ColorBufferBit); GL.MatrixMode(All.Projection); GL.LoadIdentity(); GL.MatrixMode(All.Modelview); GL.LoadIdentity(); GL.Enable(All.Texture2D); GL.GenBuffers(2, VBOs); GL.BindBuffer(All.ArrayBuffer, VBOs[0]); GL.BufferData(All.ArrayBuffer, new IntPtr(Vertices.Length Vector2.SizeInBytes), Vertices, All.StaticDraw); GL.BindBuffer(All.ArrayBuffer, VBOs[1]); GL.BufferData(All.ArrayBuffer, new IntPtr(TexCoords.Length Vector2.SizeInBytes), TexCoords, All.StaticDraw); int[] Data = new int[32 32]; for (int I = 0; I 32 32; I++) { if (I % 3 == 0) Data[I] = 255; if (I % 3 == 1) Data[I] = 65280; if (I % 3 == 2) Data[I] = 16711680; } GL.GenTextures(1, Tex); GL.BindTexture(All.Texture2D, Tex[0]); GL.TexParameter(All.Texture2D, All.TextureMinFilter, (int)All.Nearest); GL.TexParameter(All.Texture2D, All.TextureMagFilter, (int)All.Nearest); GL.TexImage2D(All.Texture2D, 0, 3, 32, 32, 0, All.Rgba, All.UnsignedByte, Data); GL.EnableClientState(All.VertexArray); GL.EnableClientState(All.TextureCoordArray); Run(); } protected override void OnRenderFrame(FrameEventArgs e) { base.OnRenderFrame(e); GL.BindBuffer(All.ArrayBuffer, VBOs[0]); GL.VertexPointer(2, All.Float, BlittableValueType.StrideOf(Vertices) IntPtr.Zero); GL.BindBuffer(All.ArrayBuffer, VBOs[1]); GL.TexCoordPointer(2, All.Float, BilittableValueType.StrideOf(TexCoords), IntPtr.Zero); GL.DrawArrays(All.Triangles, 0, 3); SwapBuffers(); } } } But there is only a white triangle Textured triangle OpenGL ES Meanwhile this code: using System; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenTK; namespace TexturedTriangle { class Program { static void Main() { GameWindow Window = new GameWindow(512, 512, GraphicsMode.Default, "Textured Triangle"); int[] Tex = new int[1]; int[] VBOs = new int[2]; Vector2[] TexCoords = { new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), }; Vector2[] Vertices = { new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 1f), }; Window.Load += (sender, e) => { GL.ClearColor(0, 0, 0, 1f); GL.Clear(ClearBufferMask.ColorBufferBit); GL.MatrixMode(MatrixMode.Projection); GL.LoadIdentity(); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); GL.Enable(EnableCap.Texture2D); GL.GenBuffers(2, VBOs); GL.BindBuffer(BufferTarget.ArrayBuffer, VBOs[0]); GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(Vertices.Length * Vector2.SizeInBytes), Vertices, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ArrayBuffer, VBOs[1]); GL.BufferData(BufferTarget.ArrayBuffer, new IntPtr(TexCoords.Length * Vector2.SizeInBytes), TexCoords, BufferUsageHint.StaticDraw); int[] Data = new int[32 * 32]; for (int I = 0; I < 32 * 32; I++) { if (I % 3 == 0) Data[I] = 255; if (I % 3 == 1) Data[I] = 65280; if (I % 3 == 2) Data[I] = 16711680; } GL.GenTextures(1, Tex); GL.BindTexture(TextureTarget.Texture2D, Tex[0]); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Three, 32, 32, 0, PixelFormat.Rgba, PixelType.UnsignedByte, Data); GL.EnableClientState(ArrayCap.VertexArray); GL.EnableClientState(ArrayCap.TextureCoordArray); }; Window.RenderFrame += (sender, e) => { GL.Clear(ClearBufferMask.ColorBufferBit); GL.BindBuffer(BufferTarget.ArrayBuffer, VBOs[0]); GL.VertexPointer(2, VertexPointerType.Float, BlittableValueType.StrideOf(Vertices), IntPtr.Zero); GL.BindBuffer(BufferTarget.ArrayBuffer, VBOs[1]); GL.TexCoordPointer(2, TexCoordPointerType.Float, BlittableValueType.StrideOf(TexCoords), IntPtr.Zero); GL.DrawArrays(PrimitiveType.Triangles, 0, 3); Window.SwapBuffers(); }; Window.Run(); } } } Works and output is Textured triangle OpenTK After GL.TexImage2D(); GL.GetError(); gives InvalidValue Documentation: www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml What the problem can be?
I find an error, InternalFormat and Format must match, else InvalidValue is given.
Use more than 1 shader on display
This question is based on the Giawa tutorials on OpenGL. I have completed tut6 & tut9 separately, but now I want to combine the two. I.e. I want that cube spinning while a bunch of stars is floating around. However, I'm pretty new two OpenGL but it looks like they use different types of shaders. So I wrote two shaders, but now when I try to run it my cube is transparent and the stars does not display. Is it possible to display both on screen at the same time correctly? Below is my code. using System; using Tao.FreeGlut; using OpenGL; using System.Windows; using System.Windows.Forms; using System.Collections.Generic; namespace OpenGLTutorial6 { class Program { private static int width = 1280, height = 720; private static ShaderProgram program, program_2; private static VBO<Vector3> cube, top_pyramid, bottom_pyramid, cubeNormals, bottom_pyramidNormals, top_pyramidNormals, star; private static VBO<Vector2> cubeUV, top_pyramidUV, bottom_pyramidUV, starUV; private static VBO<int> cubeQuads, top_pyramidTrianlges, bottom_pyramidTrianlges, starQuads; private static bool fullscreen = false; private static bool left, right, up, down; private static List<Star> stars = new List<Star>(); private static Random generator = new Random(Environment.TickCount); private static float theta = (float)Math.PI / 2, phi = (float)Math.PI / 2; private static Texture crateTexture, brickTexture, cracked_glassTexture, desert_surfaceTexture, numbersTexture, ziggyTexture, starTexture; private static System.Diagnostics.Stopwatch watch; private static float angle; private static int rotate = 1; private static bool lighting = true; private class Star { public float angle; public float dist; public Vector3 color; public Star(float Angle, float Distance, Vector3 Color) { this.angle = Angle; this.dist = Distance; this.color = Color; } } static void Main(string[] args) { // create an OpenGL window Glut.glutInit(); Glut.glutInitDisplayMode(Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH); Glut.glutInitWindowSize(width, height); Glut.glutCreateWindow("SCREENSAVER"); // provide the Glut callbacks that are necessary for running this tutorial Glut.glutIdleFunc(OnRenderFrame); //Glut.glutIdleFunc(OnRenderFrame_2); Glut.glutDisplayFunc(OnDisplay); //<<<<<<<<<<<<< KEYBOARD FUNCTIONS Glut.glutSpecialFunc(new Glut.SpecialCallback(OnKeyPress)); Glut.glutKeyboardFunc(OnKeyboardDown); Glut.glutKeyboardUpFunc(OnKeyboardUp); //<<<<<<<<<<<<< DISPOSE Glut.glutCloseFunc(OnClose); // enable depth testing to ensure correct z-ordering of our fragments Gl.Enable(EnableCap.DepthTest); Gl.Disable(EnableCap.DepthTest); Gl.Enable(EnableCap.Blend); Gl.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One); //<<<<<<<<<<< COMPILE SHADER PROGRAMS program = new ShaderProgram(VertexShader, FragmentShader); program_2 = new ShaderProgram(VertexShader_2, FragmentShader_2); //set the view and projection matrix, which are static throughout this tutorial program.Use(); program["projection_matrix"].SetValue(Matrix4.CreatePerspectiveFieldOfView(0.45f, (float)width / height, 0.1f, 1000f)); program["view_matrix"].SetValue(Matrix4.LookAt(new Vector3(0, 0, 10), Vector3.Zero, Vector3.Up)); program["light_direction"].SetValue(new Vector3(1, 1, 1)); program["enable_lighting"].SetValue(lighting); program_2.Use(); program_2["projection_matrix"].SetValue(Matrix4.CreatePerspectiveFieldOfView(0.45f, (float)width / height, 0.1f, 1000f)); program_2["view_matrix"].SetValue(Matrix4.LookAt(new Vector3(0, 0, 20), Vector3.Zero, Vector3.Up)); //<<<<<<<<< LOAD TEXTURES crateTexture = new Texture("crate.jpg"); brickTexture = new Texture("bricks.jpg"); cracked_glassTexture = new Texture("crack.jpg"); desert_surfaceTexture = new Texture("desert.jpg"); numbersTexture = new Texture("numbers.jpg"); ziggyTexture = new Texture("ziggy.jpg"); starTexture = new Texture("star.bmp"); // each star is simply a quad star = new VBO<Vector3>(new Vector3[] { new Vector3(-1, -1, 0), new Vector3(1, -1, 0), new Vector3(1, 1, 0), new Vector3(-1, 1, 0) }); starUV = new VBO<Vector2>(new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1) }); starQuads = new VBO<int>(new int[] { 0, 1, 2, 3 }, BufferTarget.ElementArrayBuffer); // create 50 stars for this tutorial int numStars = 50; for (int i = 0; i < numStars; i++) { stars.Add(new Star(0, (float)i / numStars * 4f, new Vector3(generator.NextDouble(), generator.NextDouble(), generator.NextDouble()))); } // create a crate with vertices and UV coordinates cube = new VBO<Vector3>(new Vector3[] { new Vector3(-1.5, 0, -0.5), new Vector3(-0.5, 1, -0.5), new Vector3(-0.5, 1, 0.5), new Vector3(-1.5, 0, 0.5), new Vector3(-0.5, 1, -0.5), new Vector3(0.5, 1, -0.5), new Vector3(0.5, 1, 0.5), new Vector3(-0.5, 1, 0.5), new Vector3(0.5, 1, -0.5), new Vector3(1.5, 0, -0.5), new Vector3(1.5, 0, 0.5), new Vector3(0.5, 1, 0.5), new Vector3(1.5, 0, -0.5), new Vector3(1.5, 0, 0.5), new Vector3(0.5, -1, 0.5), new Vector3(0.5, -1, -0.5), new Vector3(0.5, -1, 0.5), new Vector3(0.5, -1, -0.5), new Vector3(-0.5, -1, -0.5), new Vector3(-0.5, -1, 0.5), new Vector3(-0.5, -1, -0.5), new Vector3(-0.5, -1, 0.5), new Vector3(-1.5, 0, 0.5), new Vector3(-1.5, 0, -0.5) }); cubeUV = new VBO<Vector2>(new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 1) }); top_pyramid = new VBO<Vector3>(new Vector3[] { new Vector3(-1.5, 0, -0.5), new Vector3(-0.5, 1, -0.5), new Vector3(0, 0, -1.5), new Vector3(-0.5, 1, -0.5), new Vector3(0.5, 1, -0.5), new Vector3(0, 0, -1.5), new Vector3(0.5, 1, -0.5), new Vector3(1.5, 0, -0.5), new Vector3(0, 0, -1.5), new Vector3(1.5, 0, -0.5), new Vector3(0.5, -1, -0.5), new Vector3(0, 0, -1.5), new Vector3(0.5, -1, -0.5), new Vector3(-0.5, -1, -0.5), new Vector3(0, 0, -1.5), new Vector3(-0.5, -1, -0.5), new Vector3(-1.5, 0, -0.5), new Vector3(0, 0, -1.5) }); top_pyramidUV = new VBO<Vector2>(new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1)}); bottom_pyramid = new VBO<Vector3>(new Vector3[] { new Vector3(-1.5, 0, 0.5), new Vector3(-0.5, 1, 0.5), new Vector3(0, 0, 1.5), new Vector3(-0.5, 1, 0.5), new Vector3(0.5, 1, 0.5), new Vector3(0, 0, 1.5), new Vector3(0.5, 1, 0.5), new Vector3(1.5, 0, 0.5), new Vector3(0, 0, 1.5), new Vector3(1.5, 0, 0.5), new Vector3(0.5, -1, 0.5), new Vector3(0, 0, 1.5), new Vector3(0.5, -1, 0.5), new Vector3(-0.5, -1, 0.5), new Vector3(0, 0, 1.5), new Vector3(-0.5, -1, 0.5), new Vector3(-1.5, 0, 0.5), new Vector3(0, 0, 1.5) }); bottom_pyramidUV = new VBO<Vector2>(new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1), new Vector2(0, 0), new Vector2(1, 0), new Vector2(1, 1)}); cubeNormals = new VBO<Vector3>(new Vector3[] { new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, 1, 0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0) }); top_pyramidNormals = new VBO<Vector3>(new Vector3[]{ new Vector3(0,1,0), new Vector3(0,1,0), new Vector3(0,1,0), new Vector3(0,1,0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0) }); bottom_pyramidNormals = new VBO<Vector3>(new Vector3[]{ new Vector3(0,1,0), new Vector3(0,1,0), new Vector3(0,1,0), new Vector3(0,1,0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(0, 0, -1), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(-1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0) }); cubeQuads = new VBO<int>(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }, BufferTarget.ElementArrayBuffer); top_pyramidTrianlges = new VBO<int>(new int[] { 0,1,2, 3,4,5, 6,7,8, 9,10,11, 12,13,14, 15,16,17}, BufferTarget.ElementArrayBuffer); bottom_pyramidTrianlges = new VBO<int>(new int[] { 0,1,2, 3,4,5, 6,7,8, 9,10,11, 12,13,14, 15,16,17}, BufferTarget.ElementArrayBuffer); watch = System.Diagnostics.Stopwatch.StartNew(); Gl.BindTexture(desert_surfaceTexture); Glut.glutMainLoop(); } private static void OnClose() { // dispose of all of the resources that were created //must still update cube.Dispose(); cubeUV.Dispose(); top_pyramid.Dispose(); top_pyramidTrianlges.Dispose(); cubeQuads.Dispose(); crateTexture.Dispose(); program.DisposeChildren = true; program.Dispose(); } private static void OnDisplay() { } private static void OnRenderFrame() { // calculate how much time has elapsed since the last frame watch.Stop(); float deltaTime = (float)watch.ElapsedTicks / System.Diagnostics.Stopwatch.Frequency; watch.Restart(); // use the deltaTime to adjust the angle of the cube angle += deltaTime; // set up the OpenGL viewport and clear both the color and depth bits Gl.Viewport(0, 0, width, height); Gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // use our shader program and bind the crate texture Gl.UseProgram(program); //<<<<<<<<<<<< TOP PYRAMID // set the transformation of the top_pyramid program["model_matrix"].SetValue(Matrix4.CreateRotationY(angle * rotate)); program["enable_lighting"].SetValue(lighting); // bind the vertex positions, UV coordinates and element array Gl.BindBufferToShaderAttribute(top_pyramid, program, "vertexPosition"); Gl.BindBufferToShaderAttribute(top_pyramidNormals, program, "vertexNormal"); Gl.BindBufferToShaderAttribute(top_pyramidUV, program, "vertexUV"); Gl.BindBuffer(top_pyramidTrianlges); // draw the textured top_pyramid Gl.DrawElements(BeginMode.Triangles, top_pyramidTrianlges.Count, DrawElementsType.UnsignedInt, IntPtr.Zero); //<<<<<<<<<< CUBE // set the transformation of the cube program["model_matrix"].SetValue(Matrix4.CreateRotationY(angle *rotate)); program["enable_lighting"].SetValue(lighting); // bind the vertex positions, UV coordinates and element array Gl.BindBufferToShaderAttribute(cube, program, "vertexPosition"); Gl.BindBufferToShaderAttribute(cubeNormals, program, "vertexNormal"); Gl.BindBufferToShaderAttribute(cubeUV, program, "vertexUV"); Gl.BindBuffer(cubeQuads); // draw the textured cube Gl.DrawElements(BeginMode.Quads, cubeQuads.Count, DrawElementsType.UnsignedInt, IntPtr.Zero); //<<<<<<<<<<<< BOTTOM PYRAMID // set the transformation of the bottom_pyramid program["model_matrix"].SetValue(Matrix4.CreateRotationY(angle * rotate)); program["enable_lighting"].SetValue(lighting); // bind the vertex positions, UV coordinates and element array Gl.BindBufferToShaderAttribute(bottom_pyramid, program, "vertexPosition"); Gl.BindBufferToShaderAttribute(bottom_pyramidNormals, program, "vertexNormal"); Gl.BindBufferToShaderAttribute(bottom_pyramidUV, program, "vertexUV"); Gl.BindBuffer(bottom_pyramidTrianlges); // draw the textured bottom_pyramid Gl.DrawElements(BeginMode.Triangles, bottom_pyramidTrianlges.Count, DrawElementsType.UnsignedInt, IntPtr.Zero); Glut.glutSwapBuffers(); } private static void OnRenderFrame_2() { watch.Stop(); float deltaTime = (float)watch.ElapsedTicks / System.Diagnostics.Stopwatch.Frequency; watch.Restart(); // perform rotation of the scene depending on keyboard input if (right) phi += deltaTime; if (left) phi -= deltaTime; if (up) theta += deltaTime; if (down) theta -= deltaTime; if (theta < 0) theta += (float)Math.PI * 2; // set up the OpenGL viewport and clear both the color and depth bits Gl.Viewport(0, 0, width, height); Gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); //<<<<<<<<< STARS // make sure the shader program and texture are being used Gl.UseProgram(program_2); Gl.BindTexture(starTexture); // calculate the camera position using some fancy polar co-ordinates Vector3 position = 20 * new Vector3(Math.Cos(phi) * Math.Sin(theta), Math.Cos(theta), Math.Sin(phi) * Math.Sin(theta)); Vector3 upVector = ((theta % (Math.PI * 2)) > Math.PI) ? Vector3.Up : Vector3.Down; program_2["view_matrix"].SetValue(Matrix4.LookAt(position, Vector3.Zero, upVector)); // loop through the stars, drawing each one for (int i = 0; i < stars.Count; i++) { // set the position and color of this star program_2["model_matrix"].SetValue(Matrix4.CreateTranslation(new Vector3(stars[i].dist, 0, 0)) * Matrix4.CreateRotationZ(stars[i].angle)); program_2["color"].SetValue(stars[i].color); Gl.BindBufferToShaderAttribute(star, program_2, "vertexPosition"); Gl.BindBufferToShaderAttribute(starUV, program_2, "vertexUV"); Gl.BindBuffer(starQuads); Gl.DrawElements(BeginMode.Quads, starQuads.Count, DrawElementsType.UnsignedInt, IntPtr.Zero); // update the position of the star stars[i].angle += (float)i / stars.Count * deltaTime * 2; stars[i].dist -= 0.2f * deltaTime; // if we've reached the center then move this star outwards and give it a new color if (stars[i].dist < 0f) { stars[i].dist += 5f; stars[i].color = new Vector3(generator.NextDouble(), generator.NextDouble(), generator.NextDouble()); } } Glut.glutSwapBuffers(); } public static void OnKeyPress(int theKey, int x, int y) { switch (theKey) { //<<<<<<< ROTATE case Glut.GLUT_KEY_F5: { rotate += 1; Console.WriteLine("Hallo!"); } break; case Glut.GLUT_KEY_F6: { rotate -= 1; } break; //<<<<<<<<<< TEXTURES case Glut.GLUT_KEY_F7: { Gl.BindTexture(cracked_glassTexture); } break; case Glut.GLUT_KEY_F8: { Gl.BindTexture(desert_surfaceTexture); } break; case Glut.GLUT_KEY_F9: { Gl.BindTexture(brickTexture); } break; case Glut.GLUT_KEY_F10: { Gl.BindTexture(ziggyTexture); } break; case Glut.GLUT_KEY_F11: { Gl.BindTexture(numbersTexture); } break; } Glut.glutPostRedisplay(); } private static void OnKeyboardDown(byte key, int x, int y) { if (key == 'w') up = true; else if (key == 's') down = true; else if (key == 'd') right = true; else if (key == 'a') left = true; else if (key == 27) Glut.glutLeaveMainLoop(); } private static void OnKeyboardUp(byte key, int x, int y) { if (key == 'w') up = false; else if (key == 's') down = false; else if (key == 'd') right = false; else if (key == 'a') left = false; else if (key == 'f') { fullscreen = !fullscreen; if (fullscreen) Glut.glutFullScreen(); else { Glut.glutPositionWindow(0, 0); Glut.glutReshapeWindow(1280, 720); } } } public static string VertexShader = #" #version 130 in vec3 vertexPosition; in vec3 vertexNormal; in vec2 vertexUV; out vec3 normal; out vec2 uv; uniform mat4 projection_matrix; uniform mat4 view_matrix; uniform mat4 model_matrix; void main(void) { normal = normalize((model_matrix * vec4(floor(vertexNormal), 0)).xyz); uv = vertexUV; gl_Position = projection_matrix * view_matrix * model_matrix * vec4(vertexPosition, 1); } "; public static string FragmentShader = #" #version 130 uniform sampler2D texture; uniform vec3 light_direction; uniform bool enable_lighting; in vec3 normal; in vec2 uv; out vec4 fragment; void main(void) { float diffuse = max(dot(normal, light_direction), 0); float ambient = 0.2; float lighting = (enable_lighting ? max(diffuse, ambient) : 1); fragment = lighting * texture2D(texture, uv); } "; public static string VertexShader_2 = #" #version 130 in vec3 vertexPosition; in vec2 vertexUV; out vec2 uv; uniform mat4 projection_matrix; uniform mat4 view_matrix; uniform mat4 model_matrix; void main(void) { uv = vertexUV; gl_Position = projection_matrix * (view_matrix * model_matrix * vec4(0, 0, 0, 1) + vec4(vertexPosition.x, vertexPosition.y, vertexPosition.z, 0)); //gl_Position = projection_matrix * view_matrix * model_matrix * vec4(vertexPosition, 1); } "; public static string FragmentShader_2 = #" #version 130 uniform sampler2D texture; uniform vec3 color; in vec2 uv; out vec4 fragment; void main(void) { fragment = vec4(color * texture2D(texture, uv).xyz, 1); } "; } } Also I've added some other textures that is not in the original tutorials
From scanning through your code, it looks like you have most of what you need. To render both items at the same time, you basically have to add the rendering code that you currently have in OnRenderFrame_2() to OnRenderFrame(). Make sure that you only have one glClear() at the start, and one glutSwapBuffers() at the end. If the outline of your code currently looks like this: OnRenderFrame prepare rendering of Item 1 glViewport glClear glUseProgram(program1) render Item 1 glutSwapBuffers OnRenderFrame_2 prepare rendering of Item 2 glViewport glClear glUseProgram(program2) render Item 2 glutSwapBuffers Rearrange it like this: OnRenderFrame prepare rendering of Item 1 prepare rendering of Item 2 glViewport glClear glUseProgram(program1) render Item 1 glUseProgram(program2) render Item 2 glutSwapBuffers
Weird cube shape
I'm trying to create a cube with both indices and vertices. I'm able to draw them, but they look kinda weird. Here's my code. It has something to do with either the vertices or indices, but I'm not sure which: public void Draw(BasicEffect effect) { foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); device.SetVertexBuffer(cubeVertexBuffer); device.Indices = iBuffer; device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12); } } private void SetUpIndices() { indices = new short[36]; indices[0] = 0; indices[1] = 3; indices[2] = 2; indices[3] = 2; indices[4] = 1; indices[5] = 0; indices[6] = 4; indices[7] = 7; indices[8] = 6; indices[9] = 6; indices[10] = 5; indices[11] = 4; indices[12] = 1; indices[13] = 2; indices[14] = 6; indices[15] = 6; indices[16] = 5; indices[17] = 1; indices[18] = 4; indices[19] = 7; indices[20] = 3; indices[21] = 3; indices[22] = 0; indices[23] = 4; indices[24] = 4; indices[25] = 0; indices[26] = 1; indices[27] = 1; indices[28] = 5; indices[29] = 4; indices[30] = 3; indices[31] = 7; indices[32] = 6; indices[33] = 6; indices[34] = 2; indices[35] = 3; iBuffer = new IndexBuffer(device, typeof(short), 36, BufferUsage.WriteOnly); iBuffer.SetData(indices); } private void SetUpVertices() { vertices = new VertexPositionColor[8]; vertices[0] = new VertexPositionColor(new Vector3(0, 0, 0), color); vertices[1] = new VertexPositionColor(new Vector3(0, 1, 0), color); vertices[2] = new VertexPositionColor(new Vector3(1, 1, 0), color); vertices[3] = new VertexPositionColor(new Vector3(1, 0, 0), color); vertices[4] = new VertexPositionColor(new Vector3(0, 0, -1), color); vertices[5] = new VertexPositionColor(new Vector3(0, 1, -1), color); vertices[6] = new VertexPositionColor(new Vector3(1, 1, -1), color); vertices[7] = new VertexPositionColor(new Vector3(1, 0, -1), color); cubeVertexBuffer = new VertexBuffer(device, typeof(VertexPositionColor), 8, BufferUsage.WriteOnly); cubeVertexBuffer.SetData<VertexPositionColor>(vertices); }
I could do a wild guess and say its because of messed order of vertices in your indices (I would call them triangles further). Usually in 3d engines you have to set up order of vertices in triangles so they all are ordered same - i.e. clockwise or counter-clockwise - when you look at them from outside of shape they form. Speaking mathematically all normals of triangles in your shape should be directed either inside or outside of shape. The direction of normal tells 3d engine when to draw triangles - engine can do two times less work if it draws triangles only on one side - the insides of a solid objects in 99,99% cases are not to be seen by user. In your case look at indices 032 and 476 - they should be either 032/467 or 023/476. And so on.
SlimDX Direct3D 11 Indexing Problems
I'm trying to draw an indexed square using SlimDX and Direct3D11. I've managed to draw a square without indices, but when I swap to my indexed version I just get a blank screen. My input layout is set to only take position data (I'm essentially extending from the third tutorial on the SlimDX website) and to draw Triangle Lists. My render loop code is as follows (I am using the triangle.fx pixel and vertex shader files from the tutorial, they take vertex positions (in screen coordinates) and paint them yellow, D3D is shorthand for SlimDX.Direct3D11) //clear the render target context.ClearRenderTargetView(renderTarget, new Color4(0.5f, 0.5f, 1.0f)); context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(mesh.VertexBuffer,12, 0)); context.InputAssembler.SetIndexBuffer(mesh.IndexBuffer, Format.R16_UNorm, 0); context.DrawIndexed(mesh.indices, 0, 0); swapChain.Present(0, PresentFlags.None); "mesh" is a struct that holds a Vertex buffer, Index buffer and vertex count. The data is filled here: Vertex[] vertexes = new Vertex[4]; vertexes[0].Position = new Vector3(0, 0, 0.5f); vertexes[1].Position = new Vector3(0, 0.5f, 0.5f); vertexes[2].Position = new Vector3(0.5f, 0, 0.5f); vertexes[3].Position = new Vector3(0.5f, 0.5f, 0.5f); UInt16[] indexes = { 0, 1, 2, 1, 3, 2 }; DataStream vertices = new DataStream(12 * 4, true, true); foreach (Vertex vertex in vertexes) { vertices.Write(vertex.Position); } vertices.Position = 0; DataStream indices = new DataStream(sizeof(int) * 6, true, true); foreach (UInt16 index in indexes) { indices.Write(index); } indices.Position = 0; mesh = new Mesh(); D3D.Buffer vertexBuffer = new D3D.Buffer(device, vertices, 12 * 4, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.VertexBuffer = vertexBuffer; mesh.IndexBuffer = new D3D.Buffer(device, indices, 2 * 6, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.vertices = vertexes.GetLength(0); mesh.indices = indexes.Length; All of this is nearly identical to my unindexed square method (with the addition of index buffers and indices, and the removal of two duplicate vertices that aren't needed with indexing), but while the unindexed method draws a square, the indexed method doesn't. My current theory is that there is either something wrong with this line: mesh.IndexBuffer = new D3D.Buffer(device, indices, 2 * 6, ResourceUsage.Default, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); Or these lines: context.InputAssembler.SetIndexBuffer(mesh.IndexBuffer, Format.R16_UNorm, 0); context.DrawIndexed(mesh.indices, 0, 0);
Why don't you just use a vertex and indexbuffer for this simple example? Like this way (Directx9): VertexBuffer vb; IndexBuffer ib; vertices = new PositionColored[WIDTH * HEIGHT]; //vertex creation vb = new VertexBuffer(device, HEIGHT * WIDTH * PositionColored.SizeInBytes, Usage.WriteOnly, PositionColored.Format, Pool.Default); DataStream stream = vb.Lock(0, 0, LockFlags.None); stream.WriteRange(vertices); vb.Unlock(); indices = new short[(WIDTH - 1) * (HEIGHT - 1) * 6]; //indicies creation ib = new IndexBuffer(device, sizeof(int) * (WIDTH - 1) * (HEIGHT - 1) * 6, Usage.WriteOnly, Pool.Default, false); DataStream stream = ib.Lock(0, 0, LockFlags.None); stream.WriteRange(indices); ib.Unlock(); //Drawing device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0); device.BeginScene(); device.VertexFormat = PositionColored.Format; device.SetStreamSource(0, vb, 0, PositionColored.SizeInBytes); device.Indices = ib; device.SetTransform(TransformState.World, Matrix.Translation(-HEIGHT / 2, -WIDTH / 2, 0) * Matrix.RotationZ(angle)); device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH * HEIGHT, 0, indices.Length / 3); device.EndScene(); device.Present(); I use the mesh in another way (directx9 code again): private void CreateMesh() { meshTerrain = new Mesh(device, (WIDTH - 1) * (HEIGHT - 1) * 2, WIDTH * HEIGHT, MeshFlags.Managed, PositionColored.Format); DataStream stream = meshTerrain.VertexBuffer.Lock(0, 0, LockFlags.None); stream.WriteRange(vertices); meshTerrain.VertexBuffer.Unlock(); stream.Close(); stream = meshTerrain.IndexBuffer.Lock(0, 0, LockFlags.None); stream.WriteRange(indices); meshTerrain.IndexBuffer.Unlock(); stream.Close(); meshTerrain.GenerateAdjacency(0.5f); meshTerrain.OptimizeInPlace(MeshOptimizeFlags.VertexCache); meshTerrain = meshTerrain.Clone(device, MeshFlags.Dynamic, PositionNormalColored.Format); meshTerrain.ComputeNormals(); } //Drawing device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0); device.BeginScene(); device.VertexFormat = PositionColored.Format; device.SetTransform(TransformState.World, Matrix.Translation(-HEIGHT / 2, -WIDTH / 2, 0) * Matrix.RotationZ(angle)); int numSubSets = meshTerrain.GetAttributeTable().Length; for (int i = 0; i < numSubSets; i++) { meshTerrain.DrawSubset(i); } device.EndScene(); device.Present();