Batch Rendering Textures (+ Debugging!) // Game Engine series

Поділитися
Вставка
  • Опубліковано 10 лис 2024

КОМЕНТАРІ • 81

  • @Gymomanen
    @Gymomanen 4 роки тому +73

    There's no reason to complain about the video getting too long. Everyone's at home watching UA-cam all evenings anyway these days.

  • @djee02
    @djee02 4 роки тому +2

    A cheap way to do pseudo bindless for better batching is to have a texture array for every resolution of texture you need and just bind those. Then, in the quad vertex you also give the index in the array. I like this better than a huge atlas of everything because wrapping still works and you have no mipmap bleeding. Also it doesn't change the resource pipeline as the index can be dynamically allocated as individual textures are streamed in. You just keep a remapping table to know what slice index to put in the vertex for the requested texture.

  • @osere6432
    @osere6432 4 роки тому +3

    Congratulations on the news! I'm so happy for you!

  • @hokhyt
    @hokhyt 4 роки тому +6

    For those with OpenGL lower than 4.5, you cannot use glBindTextureUnit. So, instead you should change your texture bind function to use glActiveTexture(GL_TEXTURE0 + slot), and then bind it normally using glBindTexture(GL_TEXTURE_2D, m_RendererID).

    • @KennyTutorials
      @KennyTutorials 3 роки тому +2

      Its doesn't work for me either. Its just renders all slots form 1 to 31 with black and zero slot with white texture doesn't render at all.

    • @zeozeozevo
      @zeozeozevo Рік тому

      Thanks. Worked perfectly!

  • @simonmaracine4721
    @simonmaracine4721 4 роки тому +7

    27:45 You could try writing "// type vertex" and "// type fragment" to delimiter the shaders instead of using #. Maybe it will remove the errors from the plugin.

    • @Rems19
      @Rems19 4 роки тому +1

      I think the plugin would still expect only one shader type per file and complain about the second version line

    • @Abdo2000
      @Abdo2000 4 роки тому

      @@Rems19 You're right, just tried it

    • @resongeo9662
      @resongeo9662 Рік тому

      2 years later but thanks dude :D This was my problem

  • @dgdev1024
    @dgdev1024 Рік тому +4

    28:48 - On some graphics cards, mine included, attempting to index a sampler array with a non-constant value will yield the following GLSL error: 'sampler arrays indexed with non-constant expressions are forbidden in GLSL 1.30 and later'.
    If you are getting this error, you can try using and adapting the fragment shader code below:
    #version 330 core
    // Inputs
    in vec4 v_Color;
    in vec2 v_TexCoord;
    in float v_TexIndex;
    // Uniforms
    uniform sampler2D u_TextureSlots[16];
    // Outputs
    out vec4 o_FragColor;
    // Main Function
    void main () {
    vec4 l_Texture;
    int l_TexIndex = int(v_TexIndex);
    switch (l_TexIndex) {
    case 0: l_Texture = texture(u_TextureSlots[0], v_TexCoord); break;
    case 1: l_Texture = texture(u_TextureSlots[1], v_TexCoord); break;
    case 2: l_Texture = texture(u_TextureSlots[2], v_TexCoord); break;
    case 3: l_Texture = texture(u_TextureSlots[3], v_TexCoord); break;
    case 4: l_Texture = texture(u_TextureSlots[4], v_TexCoord); break;
    case 5: l_Texture = texture(u_TextureSlots[5], v_TexCoord); break;
    case 6: l_Texture = texture(u_TextureSlots[6], v_TexCoord); break;
    case 7: l_Texture = texture(u_TextureSlots[7], v_TexCoord); break;
    case 8: l_Texture = texture(u_TextureSlots[8], v_TexCoord); break;
    case 9: l_Texture = texture(u_TextureSlots[9], v_TexCoord); break;
    case 10: l_Texture = texture(u_TextureSlots[10], v_TexCoord); break;
    case 11: l_Texture = texture(u_TextureSlots[11], v_TexCoord); break;
    case 12: l_Texture = texture(u_TextureSlots[12], v_TexCoord); break;
    case 13: l_Texture = texture(u_TextureSlots[13], v_TexCoord); break;
    case 14: l_Texture = texture(u_TextureSlots[14], v_TexCoord); break;
    case 15: l_Texture = texture(u_TextureSlots[15], v_TexCoord); break;
    }
    o_FragColor = l_Texture * v_Color;
    }
    You'll need to include a number of switch cases equal to the number of texture slots you wish to support.
    Renderer: Mesa Intel(R) HD Graphics 620 (KBL GT2)
    OpenGL and Shading Language version: 4.6 and 4.60 (but building for version 3.3 and 3.30 respectively)
    OS: Ubuntu Studio 20.04 LTS (Linux)

    • @BilgeKagan_30-zf7cy
      @BilgeKagan_30-zf7cy Рік тому

      Thank you . This solved my problem, I know a lot of time passed since this comment but i am getting a weird scene when i try to multiply v_TexCoord with v_TilingFactor in each case like
      case 0: l_Texture = texture(u_TextureSlots[0], v_TexCoord * v_TilingFactor); break;
      Did you try to do the same ? It seems TilingFactor is not working as it supposed to

  • @freemanfreeman7841
    @freemanfreeman7841 4 роки тому +7

    please do a vedio about books or sources that helped you to become this good !

  • @lincolnkonig9324
    @lincolnkonig9324 4 роки тому

    "What has happened?" I've asked myself this exact question more times than I'd like to admit since I've started following this series

  • @samuelbartik5265
    @samuelbartik5265 4 роки тому +1

    Thanks for the new video. I'm excited!

  • @oschi3653
    @oschi3653 2 роки тому

    Thank you very much! I guess that's exactly the solution for my problem: sonar data ping of up to 20000 bytes, spread over multiple textures, pan and zoom, all be done with batch rendering textures. 👌

  • @popplestones886
    @popplestones886 2 роки тому

    Great video as usual cherno

  • @pygmee6412
    @pygmee6412 4 роки тому +1

    WebGL 1 still doesn't support indexing from non constant.
    The most efficient way to get an element from an array by dynamic index is writing something terrific like this:
    for (int i = 0 ; i < MAX_LENGTH ; i++) if (i == variable_index) return array[i];
    This works because WebGL can be sure that variable i is in the range of the array.
    Fortunately, this is no longer a thing in WebGL 2!

  • @robertocabiddu6008
    @robertocabiddu6008 4 роки тому +5

    I have a problem, which is really really weird
    I followed every step, even cloned the repo, but there are some artifacts at the end of the lower triangle whenever a "drawquad" has a different texture (for example, the first quad is a flat colored quad, the second is a checker board quad, so the checker board quad has some artifacts)
    Found out that the artifacts are not completely random, but instead it has bits of the previous texture. (following the previous example, the checker board quad would have white stuff/artifacts, because thats the previous texture)
    The size varies with the distance to the object (zooming in makes it slightly smaller, zooming out covers the whole lower triangle).
    i have no clue for what it could be. and, i dont think the problem is in the code because it works for @The Cherno, but not for me (the cloned repo)

    • @lysy-zn2gg
      @lysy-zn2gg Рік тому

      I know this comment is 2 years old but I think it could do something with resolution, precisely aspect ratio of your monitor? I remember I got something like this when I was playing in Godot

  • @Puddlestomps
    @Puddlestomps 4 роки тому +9

    Glad to see the Game Engine series picking up again!
    I just have one question: Why is the texture index variable a float? Seems like you might as well make it an int seeing as you convert to int in the sampler index in the shader anyway.

    • @98xani
      @98xani 4 роки тому +1

      Puddlestomper floating point numbers are in general more performant in gpu/shader code

    • @peterhaller5791
      @peterhaller5791 4 роки тому

      because GLSL attributes (via glVertexAttribPointer) do not support GL_INT!

    • @speesy
      @speesy 4 роки тому +3

      use glVertexAttribIPointer :)

    • @fredhair
      @fredhair 4 роки тому +5

      @@peterhaller5791 False, they do indeed support integers but it just requires using glVertexAttriblPointer. Whilst floats are generally used efficiently in matrix operations on the gpu there is no reason to use a float here at all. No math is used on the index it's just an index, the real flaw is that there are casts inside a loop meaning potentially a lot of unnecessary casts.. Hopefully your compiler is able to notice this mistake.

  • @dyslexicsoap7605
    @dyslexicsoap7605 9 місяців тому

    as far as I can tell this is the first video in the game engine series where he stops doing the weird fuckin arm thing at the start

  • @Mateus.007
    @Mateus.007 2 роки тому +2

    This video is why I will never use prefixes.

  • @konrad3688
    @konrad3688 4 роки тому +3

    FYI std::array has .fill method.

  • @samuelrasquinha6712
    @samuelrasquinha6712 4 роки тому +2

    Hey just an idea :
    In games like terraria there are like a 1000 dirt tiles and 100 stone tiles etc..
    What do you think is better to do ?
    - Draw 1000 dirt tiles in one draw call and 100 stone tiles in another draw call OR
    - The way you are doing it.. (ie : total draw calls = (1000 + 100) / total texture slots) which is wayy more than 2
    Thanks and keep up the amazing quality.

    • @kplays_6000
      @kplays_6000 4 роки тому

      With this method you'll still only use 2 textures

    • @Radgerayden-ist
      @Radgerayden-ist 4 роки тому

      The answer is actually outside the presented alternatives; you use a texture atlas and set the uvs of each quad according to the kind of tile they represent. So a single texture.

    • @pulse8112
      @pulse8112 4 роки тому +1

      But you aren't realizing that all dirt tiles and stone tiles haven't got unique texture, you are just selecting texture 1 or 2 = one draw call

  • @ЯрославТолов
    @ЯрославТолов 4 роки тому

    Not being able to samle from a non const is still a thing in webGL. Most of out users still has only wegGL 1 which is really anoing sometimes. That's also a main reason why we desided to use atlases for static geometry instead of batch everithing in one giant mesh.

  • @DavidOnonokpono
    @DavidOnonokpono 4 місяці тому

    Thanks

  • @alexkhazov7264
    @alexkhazov7264 3 роки тому

    I think there's a check missing for the max texture slots. The texture slot assignment only checks if an index was found, but not if it's within the range of the textureSlots array

  • @glennstormdesign
    @glennstormdesign 4 роки тому +1

    So _that's_ how casts work in GLSL. (thx!) And, yeah, it appears switch is not available for me, 'had to use if/elseif. As a side Q, strictly speaking if "avoiding code duplication" is a goal, and I'm hearing "and we'll put this everywhere", doesn't the quad draw sections call out for some minor refactoring?

  • @JATmatic
    @JATmatic 4 роки тому

    I quite recently implemented texture batching in my own graphics engine:
    It uses GL_TEXTURE_2D_ARRAY feature with texture atlas packing the sprites to multiple layer in single texture.
    So it consumes only single texture slot, but is drawback is that shader code gets more complicated.

    • @sebasgr6736
      @sebasgr6736 4 роки тому

      GL_TEXTURE_2D_ARRAY isnt only support a unique texture size? If you want 200 layers with 4K res, openGL allocate for 200 textures of 4K res. That is a waste of memory.

    • @andrewgeary415
      @andrewgeary415 3 роки тому

      @@sebasgr6736 using an array of textures with varying sizes causes errors on some GPUs, particularly from AMD. I'm currently trying to find an alternative as you get these terrible bleeding artifacts that are impossible to ignore.

  • @mattyvrba1725
    @mattyvrba1725 4 роки тому

    Try adding in a reload shader feature, I did it for my project, you basicly unload all shaders and load them back in, that allows you to reload shaders while editing them @Cherno

  • @KennyTutorials
    @KennyTutorials 4 роки тому

    I see this video in playlist when cherno uploads it) And wait it today)))

  • @KennyTutorials
    @KennyTutorials 3 роки тому +2

    This perfectly works on OpenGL 4.5+ with glBindTextureUnit, but in OpenGL 3.3 with glBindTexture and glActiveTexture its doesn't work for me. It can't not work at all because it has backward compatibility. That is, absolutely the same code, only the OpenGL functions are different depending on their support by the driver. Does anyone have any suggestions where the problem might be? I am sure that the problem is 100% in the textures, since everything works fine with the color.

    • @ravilefendiev4478
      @ravilefendiev4478 Рік тому

      I have the same issue, my renderer is barely different, anyway I have problem that only the texture that was bound last gets drawn, haven't you found solution yet?

    • @Greywize
      @Greywize 8 місяців тому

      ​@@ravilefendiev4478 10 months late but I resolved this issue by modifying my Bind() method in my OpenGLTexture2D class. Adding glActiveTexture(GL_TEXTURE0 + slot); before glBindTexture(GL_TEXTURE_2D, m_rendererID); fixed it for me.
      Should also mention, make sure you actually pass in the slot number to the texture when you bind them all in Flush() in Renderer2D.cpp

  • @alevidal647
    @alevidal647 3 роки тому

    "Good Guy" Cherno. 😂

  • @2dapoint424
    @2dapoint424 2 роки тому +1

    Can someone point me to opengl game programmin books that helps me with such concepts? This is the only video I found on batch textrue rendring, but I also wanted to read book which can help me understand more.

  • @rasoulsadeghi8635
    @rasoulsadeghi8635 4 роки тому

    Hi
    I am PLC programmer.
    I have question.
    in Beckhoff PLC/IPC we have ability to program with C/C++ (in visual studio environment)
    It would be a bridge between computer programming and industrial automation and robotic.
    do you have any comment/idea about it?

  • @ultimateskibum6813
    @ultimateskibum6813 4 роки тому

    Hey Cherno! Great work as always, Love the number of videos coming out right now! It really helps me cope with this social distancing. Can you please update the GitHub code though, please!

  • @FredericHamel
    @FredericHamel 3 роки тому

    I do have the sampling problem on my amd gpu I have. I need to use branch statement.

  • @dkthehunter596
    @dkthehunter596 4 роки тому

    Is it possible for other programs to use up texture slots? For example, I can poll the user's GPU, and see that it can have 32 textures, but what if the user is running another program that uses a couple of texture slots? Do I still have 32 texture slots to work with?

    • @dylanclarke9497
      @dylanclarke9497 10 місяців тому +1

      Old question but answer for others at least - yes, you have x amount of texture slots for each opengl rendering context (which is determined by various driver/opengl settings/implementations)

  • @pythonpc8090
    @pythonpc8090 4 роки тому

    Could you give your point of view about how to learn all this SE stuff when I m not actually involveed in it. I mean, we learn such things like SLC, app testing, a huge deal of different rules and definitions, but lots of them I just can`t get properly:(

  • @sam_is_people1170
    @sam_is_people1170 2 роки тому

    thanks!!!

  • @Arwahanoth
    @Arwahanoth 4 роки тому

    23:10 static operator override for a, b Ref ?

  • @nerdrage562
    @nerdrage562 4 роки тому

    Hey there! i was wondering why you're using a float attribute for the texture id. I made a batch similar batch renderer a few weeks ago and I coulnd't bind the texture with an int attribute. Later I found out that you have to use the glVertexAttrib"I"Pointer function to do that and it worked well. You have also to use the "flat" qualifier on your shaders otherwise it won't compile. I'm using OpenGL core 3.3. Hope this helps :) keep up the good work

    • @KennyTutorials
      @KennyTutorials 3 роки тому

      In OpenGL 3.3 core flat qualifier is not supported, maybe you're using forward compat.

  • @beProsto
    @beProsto 4 роки тому +1

    I've got a question. I understand that using a for loop for setting all texture slots to zero was done by you to make it easier to read for less advanced peeps in c/c++.
    What I don't understand, is why not just use a good old memset? I mean - you already used memcpy in this series, so why not use memset?
    I think it would be very easy to explain what it does, and you wouldn't sacrifice the performance, because as far as i know, some compilers don't translate fors into memsets when it's needed.
    Why did you choose to do it this way?

    • @Destroyer19941995
      @Destroyer19941995 4 роки тому +1

      You answered your own question in second sentence.

  • @goyonman9655
    @goyonman9655 4 роки тому +1

    no hand movements at the introduction??
    😲😲😲😲😲

  • @peterhaller5791
    @peterhaller5791 4 роки тому

    Great video like always. Thank you Yan! Could you please make in the near future a little video about Depth and Z Positions and Transparency of png textures because if I enable GL_DEPTH_TEST i have always strange overlapping behaviour of my textures. This would be awesome. Regards

    • @hls333555
      @hls333555 4 роки тому

      I also have this issue during batching several transparent textures with same Z position...

  • @forkercat8829
    @forkercat8829 3 роки тому

    For macOS users, it looks like OpenGL supports 16 texture slots.

  • @sharyarkhan690
    @sharyarkhan690 4 роки тому

    I lovvveeeeee youuu sirr

  • @funhuninjastudio
    @funhuninjastudio 4 роки тому +5

    Cherno, can you make a video for socket programming please?
    Like so he can see!

    • @aizuon
      @aizuon 4 роки тому

      YES PLEASE! I'd really appreciate a network lib series along with game engine series

  • @TheMrKeksLp
    @TheMrKeksLp 4 роки тому +1

    Noo!! You can't just bind endless number of textures!!!
    Hehe vulkan sampler array go brrt

    • @tompp2100
      @tompp2100 4 роки тому +1

      OpenGL has a bindless texture extension.

  • @adityapetkar3062
    @adityapetkar3062 4 роки тому

    When will you make 3d game engine.Pls reply

    • @rainbowpikmin
      @rainbowpikmin 4 роки тому

      He will do a 3D renderer for this series, just wait for 2D to finish

  • @fredhair
    @fredhair 4 роки тому +4

    17:35 Why are you using a float for your textureIndex here? It means you have to cast an int to a float potentially multiple times in your for loop and at least once in the below if block.
    21:12 Why are you casting to a Texture2D here instead of comparing 2 OpenGLTexture2D's? Doesn't seem particularly safe accessing member data that may not exist.
    Just trying to get everyone to think a bit harder about the code they're writing. If you use a low level language like C++ these are the kinda things you want to understand, otherwise use a higher level language that abstracts these refinements and probably can optimize better than you. It's all very well saying things are just temporary but then you have to keep track of every single piece of "temporary" code and change it, why not write the code safely and correctly the first time and save yourself multiple headaches later on.

    • @EnginDenizCengiz
      @EnginDenizCengiz 4 роки тому

      I believe that was because he will be creating a single vertex buffer for his batch. This buffer contains bunch of floats like position, uv and that texture index to sample from which in that case has to be a float as well. He mentioned about this in one of his batch renderer videos.

    • @jamesmnguyen
      @jamesmnguyen 4 роки тому +1

      The second point: There's rarely going to be a case where multiple graphics APIs are active at one time. So you can't compare a OpenGL texture to a DirectX texture.

    • @fredhair
      @fredhair 4 роки тому

      @@jamesmnguyen Agreed, theres no reason to use polymorphism here, frankly it's a pretty big mistake imo, it's very unsafe and I'm not sure how Cherno has deliberately written it in that way without a word about how bad it is.. I wouldn't use this even temporarily it's just awful.

    • @jamesmnguyen
      @jamesmnguyen 4 роки тому +1

      @@fredhair Im not saying the code is bad, his code allows you to swap the graphics API without using switch statements, etc, to handle each api when performing a certain operation like drawing a quad. His way is analogous to using function pointers (which is technically whats happening)

    • @fredhair
      @fredhair 4 роки тому

      @@jamesmnguyen Yes but the data member he is accessing didn't appear to be a virtual member and therefore not guaranteed to actually exist in the derived classes. Its generalising an aspect that is specific to a single class, this is unsafe and bad code IMO. If he was calling a virtual function it would of course be fine but as it is he's accessing a non virtual data member from a derived class using the bass class pointer...

  • @charlesz88
    @charlesz88 3 роки тому +1

    36:06, value,32 shows your the whole array. Learn something new :)

  • @monomere
    @monomere 4 роки тому

    huh, 2 mins ago... :D

  • @suryabond0077
    @suryabond0077 4 роки тому

    Coding is very very hell like horribly difficult :( , if u see this as a begginer(like me) u know what am sayin😐🙄😶

    • @NoobMaster-vk1mr
      @NoobMaster-vk1mr 4 роки тому +3

      Surya bond 007 eh once you get more into it it becomes way easier. If you’re a beginner I’d recommend starting with a different language than C++ though as it is pretty complicated to be your first language. I’d say that you should start with python and then once you’ve mastered that you should get into C++ and Java

  • @perpetuaL524
    @perpetuaL524 4 роки тому +1

    3d