• Advertisement
  • Popular Tags

  • Popular Now

  • Advertisement
  • Similar Content

    • By tj8146
      I am using immediate mode for OpenGL and I am creating a 2D top down car game. I am trying to configure my game loop in order to get my car-like physics working on a square shape. I have working code but it is not doing as I want it to. I am not sure as to whether it is my game loop that is incorrect or my code for the square is incorrect, or maybe both! Could someone help because I have been trying to work this out for over a day now
      I have attached my .cpp file if you wish to run it for yourself.. 
      WinMain code:
      /******************* WIN32 FUNCTIONS ***************************/ int WINAPI WinMain( HINSTANCE hInstance, // Instance HINSTANCE hPrevInstance, // Previous Instance LPSTR lpCmdLine, // Command Line Parameters int nCmdShow) // Window Show State { MSG msg; // Windows Message Structure bool done=false; // Bool Variable To Exit Loop Car car; car.x = 220; car.y = 140; car.dx = 0; car.dy = 0; car.ang = 0; AllocConsole(); FILE *stream; freopen_s(&stream, "CONOUT$", "w", stdout); // Create Our OpenGL Window if (!CreateGLWindow("OpenGL Win32 Example",screenWidth,screenHeight)) { return 0; // Quit If Window Was Not Created } while(!done) // Loop That Runs While done=FALSE { if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting? { if (msg.message==WM_QUIT) // Have We Received A Quit Message? { done=true; // If So done=TRUE break; } else // If Not, Deal With Window Messages { TranslateMessage(&msg); // Translate The Message DispatchMessage(&msg); // Dispatch The Message } } else // If There Are No Messages { if(keys[VK_ESCAPE]) done = true; void processKeys(Car& car); //process keyboard while (game_is_running) { loops = 0; while (GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) { update(car); // update variables next_game_tick += SKIP_TICKS; loops++; } display(car); // Draw The Scene SwapBuffers(hDC); // Swap Buffers (Double Buffering) } } } // Shutdown KillGLWindow(); // Kill The Window return (int)(msg.wParam); // Exit The Program } //WIN32 Processes function - useful for responding to user inputs or other events. LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window UINT uMsg, // Message For This Window WPARAM wParam, // Additional Message Information LPARAM lParam) // Additional Message Information { switch (uMsg) // Check For Windows Messages { case WM_CLOSE: // Did We Receive A Close Message? { PostQuitMessage(0); // Send A Quit Message return 0; // Jump Back } break; case WM_SIZE: // Resize The OpenGL Window { reshape(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height return 0; // Jump Back } break; case WM_LBUTTONDOWN: { mouse_x = LOWORD(lParam); mouse_y = screenHeight - HIWORD(lParam); LeftPressed = true; } break; case WM_LBUTTONUP: { LeftPressed = false; } break; case WM_MOUSEMOVE: { mouse_x = LOWORD(lParam); mouse_y = screenHeight - HIWORD(lParam); } break; case WM_KEYDOWN: // Is A Key Being Held Down? { keys[wParam] = true; // If So, Mark It As TRUE return 0; // Jump Back } break; case WM_KEYUP: // Has A Key Been Released? { keys[wParam] = false; // If So, Mark It As FALSE return 0; // Jump Back } break; } // Pass All Unhandled Messages To DefWindowProc return DefWindowProc(hWnd,uMsg,wParam,lParam); }  
      C++ and OpenGL code:
      int mouse_x=0, mouse_y=0; bool LeftPressed = false; int screenWidth=1080, screenHeight=960; bool keys[256]; float radiansFromDegrees(float deg) { return deg * (M_PI / 180.0f); } float degreesFromRadians(float rad) { return rad / (M_PI / 180.0f); } bool game_is_running = true; const int TICKS_PER_SECOND = 50; const int SKIP_TICKS = 1000 / TICKS_PER_SECOND; const int MAX_FRAMESKIP = 10; DWORD next_game_tick = GetTickCount(); int loops; typedef struct { float x, y; float dx, dy; float ang; }Car; //OPENGL FUNCTION PROTOTYPES void display(const Car& car); //called in winmain to draw everything to the screen void reshape(int width, int height); //called when the window is resized void init(); //called in winmain when the program starts. void processKeys(Car& car); //called in winmain to process keyboard input void update(Car& car); //called in winmain to update variables /************* START OF OPENGL FUNCTIONS ****************/ void display(const Car& car) { const float w = 50.0f; const float h = 50.0f; glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(100, 100, 0); glBegin(GL_POLYGON); glVertex2f(car.x, car.y); glVertex2f(car.x + w, car.y); glVertex2f(car.x + w, car.y + h); glVertex2f(car.x, car.y + h); glEnd(); glFlush(); } void reshape(int width, int height) // Resize the OpenGL window { screenWidth = width; screenHeight = height; // to ensure the mouse coordinates match // we will use these values to set the coordinate system glViewport(0, 0, width, height); // Reset the current viewport glMatrixMode(GL_PROJECTION); // select the projection matrix stack glLoadIdentity(); // reset the top of the projection matrix to an identity matrix gluOrtho2D(0, screenWidth, 0, screenHeight); // set the coordinate system for the window glMatrixMode(GL_MODELVIEW); // Select the modelview matrix stack glLoadIdentity(); // Reset the top of the modelview matrix to an identity matrix } void init() { glClearColor(1.0, 1.0, 0.0, 0.0); //sets the clear colour to yellow //glClear(GL_COLOR_BUFFER_BIT) in the display function //will clear the buffer to this colour. } void processKeys(Car& car) { if (keys[VK_UP]) { float cdx = sinf(radiansFromDegrees(car.ang)); float cdy = -cosf(radiansFromDegrees(car.ang)); car.dx += cdx; car.dy += cdy; } if (keys[VK_DOWN]) { float cdx = sinf(radiansFromDegrees(car.ang)); float cdy = -cosf(radiansFromDegrees(car.ang)); car.dx += -cdx; car.dy += -cdy; } if (keys[VK_LEFT]) { car.ang -= 2; } if (keys[VK_RIGHT]) { car.ang += 2; } } void update(Car& car) { car.x += car.dx*next_game_tick; }  
      game.cpp
    • By tj8146
      I am using immediate mode for OpenGL and I am creating a 2D top down car game. I am trying to configure my game loop in order to get my car-like physics working on a square shape. I have working code but it is not doing as I want it to. I am not sure as to whether it is my game loop that is incorrect or my code for the square is incorrect, or maybe both! Could someone help because I have been trying to work this out for over a day now
      I have attached my .cpp file if you wish to run it for yourself.. 
       
      This is my C++ and OpenGL code:
      int mouse_x=0, mouse_y=0; bool LeftPressed = false; int screenWidth=1080, screenHeight=960; bool keys[256]; float radiansFromDegrees(float deg) { return deg * (M_PI / 180.0f); } float degreesFromRadians(float rad) { return rad / (M_PI / 180.0f); } bool game_is_running = true; const int TICKS_PER_SECOND = 50; const int SKIP_TICKS = 1000 / TICKS_PER_SECOND; const int MAX_FRAMESKIP = 10; DWORD next_game_tick = GetTickCount(); int loops; typedef struct { float x, y; float dx, dy; float ang; }Car; //OPENGL FUNCTION PROTOTYPES void display(const Car& car); //called in winmain to draw everything to the screen void reshape(int width, int height); //called when the window is resized void init(); //called in winmain when the program starts. void processKeys(Car& car); //called in winmain to process keyboard input void update(Car& car); //called in winmain to update variables /************* START OF OPENGL FUNCTIONS ****************/ void display(const Car& car) { const float w = 50.0f; const float h = 50.0f; glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(100, 100, 0); glBegin(GL_POLYGON); glVertex2f(car.x, car.y); glVertex2f(car.x + w, car.y); glVertex2f(car.x + w, car.y + h); glVertex2f(car.x, car.y + h); glEnd(); glFlush(); } void reshape(int width, int height) // Resize the OpenGL window { screenWidth = width; screenHeight = height; // to ensure the mouse coordinates match // we will use these values to set the coordinate system glViewport(0, 0, width, height); // Reset the current viewport glMatrixMode(GL_PROJECTION); // select the projection matrix stack glLoadIdentity(); // reset the top of the projection matrix to an identity matrix gluOrtho2D(0, screenWidth, 0, screenHeight); // set the coordinate system for the window glMatrixMode(GL_MODELVIEW); // Select the modelview matrix stack glLoadIdentity(); // Reset the top of the modelview matrix to an identity matrix } void init() { glClearColor(1.0, 1.0, 0.0, 0.0); //sets the clear colour to yellow //glClear(GL_COLOR_BUFFER_BIT) in the display function //will clear the buffer to this colour. } void processKeys(Car& car) { if (keys[VK_UP]) { float cdx = sinf(radiansFromDegrees(car.ang)); float cdy = -cosf(radiansFromDegrees(car.ang)); car.dx += cdx; car.dy += cdy; } if (keys[VK_DOWN]) { float cdx = sinf(radiansFromDegrees(car.ang)); float cdy = -cosf(radiansFromDegrees(car.ang)); car.dx += -cdx; car.dy += -cdy; } if (keys[VK_LEFT]) { car.ang -= 2; } if (keys[VK_RIGHT]) { car.ang += 2; } } void update(Car& car) { car.x += car.dx*next_game_tick; } My WinMain code:
      /******************* WIN32 FUNCTIONS ***************************/ int WINAPI WinMain( HINSTANCE hInstance, // Instance HINSTANCE hPrevInstance, // Previous Instance LPSTR lpCmdLine, // Command Line Parameters int nCmdShow) // Window Show State { MSG msg; // Windows Message Structure bool done=false; // Bool Variable To Exit Loop Car car; car.x = 220; car.y = 140; car.dx = 0; car.dy = 0; car.ang = 0; AllocConsole(); FILE *stream; freopen_s(&stream, "CONOUT$", "w", stdout); // Create Our OpenGL Window if (!CreateGLWindow("OpenGL Win32 Example",screenWidth,screenHeight)) { return 0; // Quit If Window Was Not Created } while(!done) // Loop That Runs While done=FALSE { if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting? { if (msg.message==WM_QUIT) // Have We Received A Quit Message? { done=true; // If So done=TRUE break; } else // If Not, Deal With Window Messages { TranslateMessage(&msg); // Translate The Message DispatchMessage(&msg); // Dispatch The Message } } else // If There Are No Messages { if(keys[VK_ESCAPE]) done = true; void processKeys(Car& car); //process keyboard while (game_is_running) { loops = 0; while (GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) { update(car); // update variables next_game_tick += SKIP_TICKS; loops++; } display(car); // Draw The Scene SwapBuffers(hDC); // Swap Buffers (Double Buffering) } } } // Shutdown KillGLWindow(); // Kill The Window return (int)(msg.wParam); // Exit The Program } //WIN32 Processes function - useful for responding to user inputs or other events. LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window UINT uMsg, // Message For This Window WPARAM wParam, // Additional Message Information LPARAM lParam) // Additional Message Information { switch (uMsg) // Check For Windows Messages { case WM_CLOSE: // Did We Receive A Close Message? { PostQuitMessage(0); // Send A Quit Message return 0; // Jump Back } break; case WM_SIZE: // Resize The OpenGL Window { reshape(LOWORD(lParam),HIWORD(lParam)); // LoWord=Width, HiWord=Height return 0; // Jump Back } break; case WM_LBUTTONDOWN: { mouse_x = LOWORD(lParam); mouse_y = screenHeight - HIWORD(lParam); LeftPressed = true; } break; case WM_LBUTTONUP: { LeftPressed = false; } break; case WM_MOUSEMOVE: { mouse_x = LOWORD(lParam); mouse_y = screenHeight - HIWORD(lParam); } break; case WM_KEYDOWN: // Is A Key Being Held Down? { keys[wParam] = true; // If So, Mark It As TRUE return 0; // Jump Back } break; case WM_KEYUP: // Has A Key Been Released? { keys[wParam] = false; // If So, Mark It As FALSE return 0; // Jump Back } break; } // Pass All Unhandled Messages To DefWindowProc return DefWindowProc(hWnd,uMsg,wParam,lParam); }  
      game.cpp
    • By lxjk
      Hi guys,
      There are many ways to do light culling in tile-based shading. I've been playing with this idea for a while, and just want to throw it out there.
      Because tile frustums are general small compared to light radius, I tried using cone test to reduce false positives introduced by commonly used sphere-frustum test.
      On top of that, I use distance to camera rather than depth for near/far test (aka. sliced by spheres).
      This method can be naturally extended to clustered light culling as well.
      The following image shows the general ideas

       
      Performance-wise I get around 15% improvement over sphere-frustum test. You can also see how a single light performs as the following: from left to right (1) standard rendering of a point light; then tiles passed the test of (2) sphere-frustum test; (3) cone test; (4) spherical-sliced cone test
       

       
      I put the details in my blog post (https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html), GLSL source code included!
       
      Eric
    • By Fadey Duh
      Good evening everyone!

      I was wondering if there is something equivalent of  GL_NV_blend_equation_advanced for AMD?
      Basically I'm trying to find more compatible version of it.

      Thank you!
    • By Jens Eckervogt
      Hello guys, 
       
      Please tell me! 
      How do I know? Why does wavefront not show for me?
      I already checked I have non errors yet.
      using OpenTK; using System.Collections.Generic; using System.IO; using System.Text; namespace Tutorial_08.net.sourceskyboxer { public class WaveFrontLoader { private static List<Vector3> inPositions; private static List<Vector2> inTexcoords; private static List<Vector3> inNormals; private static List<float> positions; private static List<float> texcoords; private static List<int> indices; public static RawModel LoadObjModel(string filename, Loader loader) { inPositions = new List<Vector3>(); inTexcoords = new List<Vector2>(); inNormals = new List<Vector3>(); positions = new List<float>(); texcoords = new List<float>(); indices = new List<int>(); int nextIdx = 0; using (var reader = new StreamReader(File.Open("Contents/" + filename + ".obj", FileMode.Open), Encoding.UTF8)) { string line = reader.ReadLine(); int i = reader.Read(); while (true) { string[] currentLine = line.Split(); if (currentLine[0] == "v") { Vector3 pos = new Vector3(float.Parse(currentLine[1]), float.Parse(currentLine[2]), float.Parse(currentLine[3])); inPositions.Add(pos); if (currentLine[1] == "t") { Vector2 tex = new Vector2(float.Parse(currentLine[1]), float.Parse(currentLine[2])); inTexcoords.Add(tex); } if (currentLine[1] == "n") { Vector3 nom = new Vector3(float.Parse(currentLine[1]), float.Parse(currentLine[2]), float.Parse(currentLine[3])); inNormals.Add(nom); } } if (currentLine[0] == "f") { Vector3 pos = inPositions[0]; positions.Add(pos.X); positions.Add(pos.Y); positions.Add(pos.Z); Vector2 tc = inTexcoords[0]; texcoords.Add(tc.X); texcoords.Add(tc.Y); indices.Add(nextIdx); ++nextIdx; } reader.Close(); return loader.loadToVAO(positions.ToArray(), texcoords.ToArray(), indices.ToArray()); } } } } } And It have tried other method but it can't show for me.  I am mad now. Because any OpenTK developers won't help me.
      Please help me how do I fix.

      And my download (mega.nz) should it is original but I tried no success...
      - Add blend source and png file here I have tried tried,.....  
       
      PS: Why is our community not active? I wait very longer. Stop to lie me!
      Thanks !
  • Advertisement
  • Advertisement
Sign in to follow this  

OpenGL Minimizing draw calls and passing transform/material data to a shader in OpenGL 3.x+

This topic is 558 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Since I'm targeting GL3.x, this somewhat limits my options of how to do certain things. 

 

In particular, I'm using glMultiDrawElements(), which has a the drawback of not being able to handle instancing info. As far as I can tell all combined draw calls that do support instancing are GL4.x. Is there something I might be missing? A simple workaround here would be to manually "write out" or expand the instances in the index/count arrays passed to glMultiDrawElements(). 

 

The bigger problem I'm having is matching my transform data up with respective objects. As best I can tell, using a TBO is the simplest way to get my matrices to the shader. Once there, extracting my world matrix is a bit trickier, though, as I still need an index of the range currently being processed (effectively in a way a combination of object/instance ID). This doesn't seem to be passed in via gl_InstanceID or any other way. How can I get around this?

 

I'd appreciate some input in terms of how to store and update material data in the shader. The standard approach seems to be to store this stuff in a UBO. These are capped to fairly small sizes though. What's your approach for dealing with complex multimaterial objects? More drawcalls and more frequent material lookup rebinding?

Share this post


Link to post
Share on other sites
Advertisement

Keep in mind that simple implementations of multi-draw will do it on the client (CPU) side, just as a loop that calls the corresponding draw function a bunch of times. GL4-level hardware will be more likely to do this loop on the GPU-side.
Note that on GPUs that do perform multi-draws on the GPU-side, you can performantly emulate instancing by issuing one draw per instance.
 
To get the best milage, you want something like ARB_shader_draw_parameters, which gives you gl_DrawId (like gl_InstanceId).
 
If this extension is not present, you may actually be better off using GL3/GL2-style instancing...

I'd appreciate some input in terms of how to store and update material data in the shader. The standard approach seems to be to store this stuff in a UBO

Material data tends to be pretty small...

Share this post


Link to post
Share on other sites

Ah if I could point you to some older thread that was about this kind of thing. Sadly I cant seem to be able to search keywords in my own posts only, so I can't find it.

 

Anyway, ARB_shader_draw_parameters seems to not be supported in any GL 3 hardware (and I've read for some reason it has a non-negligible performance impact). So the idea is to work around that using this beautifully named draw call:

 

glDrawElementsInstancedBaseVertexBaseInstance

 

Which comes in the extension arb_base_instance, made core in 4.2, which also is supported by all the GL 3 hardware you should care about.

 

Now this gives you a couple of things:

 

A way to specify a vertex offset to start drawing from in a vertex buffer.

A way to specify an index offset to start drawing from in an index buffer.

A way to specify the instance you're starting to draw from in instanced rendering.

 

With this you can have a single VAO, with all your meshes, and a way to combine instanced rendering with normal rendering in a single draw call (like Vulkan!).

 

You need a way to bind these single/multiple mesh instances to their respective transform/material data right? And uploading a single uniform per draw call wont cut it, since you might have instanced calls with more than one instance. So you need a way to upload data for several different instances, preferably without caring if they're of the same mesh or not, so you can issue a single upload then make draw calls as needed that operate with that uploaded data.

 

Now the issue here is that instance ID still works as usual, that is, its zero based. So if you're drawing 5 instances of mesh 10 in your draw list, you need it to fetch transform data at index 10, 11, 12, 13 and 14, so you need a way to communicate to the shader that it should start from index 10. So, with a normal instanced draw call, if you tell it to draw 5 instances, it will start at zero.

 

A way to get around this is to specify a separate instanced attribute buffer, that contains the indices. From 0 to whatever maximum instances you can draw in a single uniform buffer upload (usually 4096 with a 64Kb UBO limit).

 

Now the trick here is to tell the instanced draw call to start at instance 10, which will fetch the instanced attribute at index 10, which will be the real index you want (ten!). gl_InstanceID will still be zero, but you dont care about that, because you got the index you want automatically fetched from the instanced attribute buffer.

 

So your render loop becomes something like:

 


for ( allPassedShaderPrograms) {

  program.bind();

  do {

    // Fill the UBOs as much as you can with the render tasks data.

    for (allUniformBufferInThisBatch)

      update(renderTask);

    // Draw all the render tasks that had their data uploaded. 

    for (allTasksThatCouldBeUpdated)

      draw(renderTask);

  // And repeate while there are tasks to draw.

  } while (thereAreMoreTasksToDraw);

}

 

You can find a more detailed explanation here if you read the PDF: http://www.gamedev.net/blog/2042/entry-2261259-from-yaml-to-renderer-in-50ms/

 

Have in mind that my renderer is pretty basic, no multi layered materials or anything, so your mileage may vary. But those are the basics to get more draw calls per buffer upload.

 

EDIT: Also, these kinds of approaches were described in nVidia's advanced OpenGL scene rendering presentations from GTC, they did one and updated it each year with different methodologies and benchmarks. You can google those. The idea is more or less the same, how to minimize buffer uploads, how to get the most out of your drawcalls, and how to efficiently upload instance IDs for indexed resources (whether they're UBOs, TBOs, SSBOs, etc).

Edited by TheChubu

Share this post


Link to post
Share on other sites

Thanks for the thorough response, TheChubu! I got a chance to look into ARB_shader_draw_parameters and even my GTX 960M doesn't have it, so I don't see a reason to add a codepath for it. I still haven't had the chance to sit down and work on the actual code, but in the very least ARB_base_instance is present, which is indeed there since GL3.1. PS - I appreciate the link to your render loop architecture.

 

As for materials: frankly I have been working on the deep innards of my game thus far and the visual side is in dire need of an upgrade from a texture-based to an actual material-enabled approach. I generally try to minimize code iteration and time spent on reimplementing features as much as possible. As such, I'd like to upgrade my render pipeline to draw meshes with multiple materials as well as extend it to handle layered materials in one swell swoop. I may be misguided here, but given the evidently fairly small maximum array size in shaders of the time this introduces a potentially limiting complication to the pipeline in order to not reduce the whole effort of minimizing draw calls to constantly remapping materials, which kinda defeats the whole purpose. In any case, I haven't gotten to adding materials to my shader pipeline yet, so it was more of a preparatory "best practices" type of question :).

Share this post


Link to post
Share on other sites

64kB UBO is the standard for nVidia (Intel varies from 16kB to 64kB, chances are you dont care about the Intel GPUs with 16kB UBOs). GCN supports 2Gb UBOs if you want them for some reason. As I mentioned, you could just simply use texture buffer objects (TBOs), which are megabyte sized in most cases (they're often used for skeletal animation data for example, which is a lot of matrices, more than whatever material data you might have).

 

Also have in mind this: 64kB (or whatever) is the maximum range you can bind of a buffer to an UBO slot, that doesnt prevents you from creating a megabyte sized buffer (which I do), and updating it entirely in a single call. Then you just rebind ranges incrementally and draw as you go (drawcalls themselves aren't the real issue here but the stuff you have to touch to get to that drawcall).

 

At least in my profiling, the time is spent updating the buffers rather than issuing the drawcalls. So that's what gets minimized.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement