{"id":22669129,"url":"https://github.com/programmersteve/openglpractice","last_synced_at":"2025-06-27T20:33:10.305Z","repository":{"id":211526695,"uuid":"728909829","full_name":"ProgrammerSteve/OpenGLPractice","owner":"ProgrammerSteve","description":"Following along to a tutorial by the Cherno youtube channel to learn opengl","archived":false,"fork":false,"pushed_at":"2023-12-14T05:41:23.000Z","size":554,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-04T10:10:24.818Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ProgrammerSteve.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-08T00:55:48.000Z","updated_at":"2023-12-08T00:56:29.000Z","dependencies_parsed_at":"2025-02-04T09:29:47.996Z","dependency_job_id":"15da6f4f-199f-43cf-8f89-e4449399f501","html_url":"https://github.com/ProgrammerSteve/OpenGLPractice","commit_stats":null,"previous_names":["programmersteve/openglpractice"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ProgrammerSteve/OpenGLPractice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammerSteve%2FOpenGLPractice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammerSteve%2FOpenGLPractice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammerSteve%2FOpenGLPractice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammerSteve%2FOpenGLPractice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ProgrammerSteve","download_url":"https://codeload.github.com/ProgrammerSteve/OpenGLPractice/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammerSteve%2FOpenGLPractice/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262327219,"owners_count":23294226,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-09T15:18:41.458Z","updated_at":"2025-06-27T20:33:10.233Z","avatar_url":"https://github.com/ProgrammerSteve.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OpenGL Notes\n\nhttps://www.glfw.org/\nhttps://glew.sourceforge.net/\n\n- Windows uses directX/direct3D for OpenGL graphics\nturn modern OpenGL functions into window's OpenGL\naccess driver dll files and retrive functions\n\n- We will use another library called Glew (openGL extension wrangler)\nprovide openGL specifications in a header file\nthe c file looks at drivers and gives you the proper code to work with them\n\n- An alternative to Glew is Glad\n\n# Vertex Buffer\n- Vertex buffer can be thought of as a buffer of memory in openGL, or a blob of memory\n- to store bytes. The difference is that it's in our GPU, in our VRam (video ram)\n- Helps you to define a bunch of data to represent your triangles\n- Put it in the GPU Vram, then issues something called a draw call\n- a draw command, that reads the buffer and draws it, we also have to tell\n- our gpu how to read the data and how to display it on the screen\n\n# Shader\n- A program that runs on the GPU\n\n\n-OpenGL operates like a state machine. You set a series of states (buffers) and draw a your triangle\n\n\n## select a buffer:\nunsigned int buffer;\nglGenBuffers(1, \u0026buffer);\n\n## bind the buffer:\nglBindBuffer(GL_ARRAY_BUFFER, buffer);\n\n## specify data for buffer\nfloat positions[6] = {\n    -0.5f, -0.5f,\n    0.0f, 0.5f,\n    0.5f, -0.5f\n};\nglBufferData(GL_ARRAY_BUFFER, 6*sizeof(float), positions,GL_STATIC_DRAW);\n\n## draw call\n- glDrawArrays(); for when we have no index buffer\n- glDrawElements(); for when we have an index buffer\n- \n\n## vertex attribute pointer\n- describes the layout of the data in the buffer\n- if you write a float in c++, we know it's a float since it's declared that way\n- if you then cast the float as an integer, it will be interpreted as an integer\n- That's also how it works in OpenGL with \n- the first x bytes for y and then next n bytes are for m\n- The function in openGL is glVertexAttribPointer() does this operation\n\n## What is a vertex\n- a vertex is a point on your geometry and isn't always just spatial coordinates\n- They usually include position data, but can have more information appended\n- (x1,y1) (x1,y1,z1) (x1,y1,z1,u1,v1) are all examples of vertices\n- each piece of information in a vertex is referred to as attributes of the vertex\n\n\n\n\n\n\n## glVertexAttribPointer()\n\n- Index, the first parameter, specifies the index of the generic vertex attribute to be modified. This is an index to\nwhich attribute you are referring to. Suppose your vertex has position data, texture data, and normal data, you need to\nsay I want the position to be at index 0, texture at index 1, and normal at index 2\n\n- size, the second parameter, specifies the number of components per generic vertex attribute, must be 1,2,3,4\nadditionally, the constant GL_BGRA is accepted. the initial value is 4. Has nothing to do with bytes, it's basically\nthe count. In our Vector2 vertices, we have 2 floats making the count equal to 2 for the size.\n\n- type, the third parameter, specifies the data tpe of each component in the array. the constants, GL_BYTE, GL_UNSIGNED_BYTE,\nGL_SHORT, GL_UNSIGNED_SHORT, GL_INT, and GL_UNSIGNED_INT are accepted. There are also a few more such as GL_HALF_FLOAT.\nIn our Vector2 vertices we are using GL_FLOAT\n\n- normalized, the fourth parameter, a boolean specifies whether the fixed-point data values should be normalized(GL_TRUE) or converted\ndirectedly as a fixed-point value (GL_FALSE) when they are accessed. Our data is already normalized -1\u003c=n\u003c=1, but let's say\nyou have a rgb color of 0-255, setting this to true will normalize the value for you.\n\n- stride, the fifth parameter, specifies the byte offset between the consecutive genric vertex attributes. if stride is 0, the\ngeneric vertex attribute are understood to be tightly packed in the array. The initial value is 0. The amount of bytes between\neach vertex. Let's suppose you have 3 floats for position, 2 floats for texture, and 3 floats for normal.\nThen thats 12 bytes for the position, 8 bytes for texture coordinates and 12 bytes for vertex normal.\n\n- pointer, the sixth parameter, specifies the offset of the first comonent of the firt generic vertex attribute in the array in\nthe data store of the buffer currently bound to the GL_ARRAY_BUFFER target. Initial value is 0. The pointer is the\npointer to the actual attribute and is inside the space of the vertex.\n\n## glEnableVertextAttribArray\nTo make the VertexAttribPointer work, you need to use glEnableVertextAttribArray() first\nthis enables a vertex attribute, glEnableVertexAttribArray(0);\nThe only parameter is an index which specifies the index of the generic vertex attribute to be enabled or disabled\nSince we only only after the position attribute, it would be i=0. Can be done before or after the \npointer code is written due to it being a state machine and it won't affect the code.\n\n\n# How do Shaders Work in OpenGL\nA program that runs on your gpu. We want to utilize the power of the GPU to output graphics. There are some\nthings you want to do on the CPU and some things you want to do on the GPU.\n\nThere are vertex shaders and fragment shaders are the ones you will be using 90% of the time. There are other kinds such\nas tessellation shaders, geometry shaders, compute shaders. When you get into more advanced stuff, the other shaders come\nin handy to know.\n\nWhen you issue a draw call, the Vertex shader will get called and then the fragment shader. There are some smaller\nsteps in between, but this is essentially what happens. The code that is our vertex shader gets called for every vertex\nwe have. So if there's 3 vertices, the vertex shader gets called 3 times. The vertex shader will tell the GPU where you \nwant your vertex to be on your window. Doesn't have much to do with graphics, but it is used to passed data to the next\nphase, which is our fragment shader.\n\nThe fragments shaders deals with pixels and will run once for each pixel that gets rasterized, or drawn on the screen.\nThe fragment shader determine the right color for each pixel and get called thousands of times. Doing calculations in\nthe fragment shader isn't ideal and would be more optimal to do the calculation in the vertex shader instead. Some things\nneed to be calculated per pixel, such as lighting, due to the texture and material.\n\nShaders work on the state machine. If you want to use a shader, you enable that shader.\nJust like you send data from the CPU to the GPU through a vertex buffer\nYou can send data to your shader in the form of a uniform that comes from the CPU\n\nSome shaders can get to thousands lines of code, and some game engines create shaders on the fly depending on the\nsettings the user selects.\n\n\n## index buffers\nAssuming we are trying to draw a square. triangles are the lowest number of vertices needed to make a plane and are\nused by GPUs make larger shapes. In order to make a square, we can just use two triangles.\n\n```\n   float positions[12] = {\n    -0.5f, -0.5f,\n    0.5f, -0.5f,\n    0.5f, 0.5f,\n\n    0.5f, 0.5f,\n    -0.5f, 0.5f,\n    -0.5f, -0.5f,\n    };\n```\nWe see that some of the same vertices can be seen in both triangles when drawing a square and we are storing the same bytes\ntwice for two of the vertices. Index buffers let us reuse existing vertices to deal with the overlap.\n\nSome vertices can be very big, such as including info for position, textures, normals, colors, etc. So storing a few thousand\nvertices more than once can add up.\n\n\n```\n    float positions[] = {\n        -0.5f, -0.5f,//0\n        0.5f, -0.5f,//1\n        0.5f, 0.5f,//2\n        -0.5f, 0.5f//3\n    };\n\n    //using the indices of the position array,\n    //we can draw the triangles as such\n    unsigned int indices[] = {\n        0,1,2,  //drawing the first triangle\n        2,3,0   //drawing the second triangle\n    };\n\n    //use GL_ELEMENT_ARRAY_BUFFER instead of GL_ARRAY_BUFFER for indices\n    //index buffers have to be made up of unsigned ints\n    unsigned int ibo; //index buffer object\n    glGenBuffers(1, \u0026ibo);\n    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);\n    glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(unsigned int), indices, GL_STATIC_DRAW);\n```\n\n\nTo do our draw call:\nWe do not use ```glDrawArrays(GL_TRIANGLES,0,6);``` anymore, instead we use ```glDrawElements()```\n```\n    //Drawing with index buffers\n    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);\n```\n\n## Dealing with Errors in OpenGL\nTwo main ways to get errors\n\nglGetError(), is compatible with all versions and sets error flags. Will give you one error at a time\n\nglDebugMessageCallback(), came out in OpenGL 4.3. Much better than getGetError() but can't be used\nin earlier versions. Will give you more detailed information on the error itself.\n\n\nglGetError() is called inside a loop to get all the errors\n```\nstatic void GLClearError()\n{\n    //runs through all the errors to clear it\n    while (glGetError() != GL_NO_ERROR);\n}\n\nstatic void GLCheckError()\n{\n    //as long as the error is not false\n    while (GLenum error =glGetError()) {\n        std::cout \u003c\u003c \"[OpenGL Error] (\" \u003c\u003c error \u003c\u003c \")\" \u003c\u003c std::endl;\n    }\n}\n```\nWe call GLClearError first, then GLCheckError to look for an error for a certain\nsection of the code. We get an error code back.\nWe need to convert the error code into hexadecimal to look it up\n\nExample:\nerror code: 1280 is 0x0500, which is GL_INVALID_ENUM\n```\n    GLClearError();\n    glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);\n    GLCheckError();\n```\nYou could write code to convert the error message code into the\nexact error message if you wanted to. The only thing we don't know\nis what line of code the error happened.\n\nIf the error is being printed in every frame, the error\nis in the render while loop.\n\nWe can make our debugger to pause the code execution when an error occurs\nusing an INSERT. It's similar to a breakpoint, but doing it with code\n\nWe make a macro at the top\n```\n//MSVC specific\n#define ASSERT(x) if (!(x)) __debugbreak(); \n```\nThis only works with MSVC compilers\nThen we change our GLCheckError function into this:\n```\nstatic bool GLLogCall()\n{\n    //as long as the error is not false\n    while (GLenum error =glGetError()) {\n        std::cout \u003c\u003c \"[OpenGL Error] (\" \u003c\u003c error \u003c\u003c \")\" \u003c\u003c std::endl;\n        return false;\n    }\n    return true;\n}\n```\nIt returns false if there's an error, and true if there's no error\nRun the code with the debugger to see the breakpoint being\ninserted with the macro\n\nNow here's the updated code:\n```\n    GLClearError();\n    glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr);\n    ASSERT(GLLogCall());\n```\n\nWe can do better and make another macro to do the clearing and checking all in\na single call.\n```\n#define GLCall(x) GLClearError();\\\n    x;\\\n    ASSERT(GLLogCall())\n```\nThe code we want goes in the middle as \"x\" in between GLCall() and GLLogCall()\nNow our code looks like this:\n```\nGLCall(glDrawElements(GL_TRIANGLES, 6, GL_INT, nullptr));\n```\n\n\nWe can do more with macros to include more information, first let's add more parameters\ninto GLLogCall:\n```\nstatic bool GLLogCall(const char* function, const char* file, int line)\n{\n    //as long as the error is not false\n    while (GLenum error =glGetError()) {\n        std::cout \u003c\u003c \"[OpenGL Error] (\" \u003c\u003c error \u003c\u003c \"): \" \n            \u003c\u003cfunction\u003c\u003c\" \"\u003c\u003cfile\u003c\u003c\":\"\u003c\u003cline \u003c\u003c std::endl;\n        return false;\n    }\n    return true;\n}\n```\nThen for the macro we have\n```\n#define GLCall(x) GLClearError();\\\n    x;\\\n    ASSERT(GLLogCall(#x, __FILE__, __LINE__))\n```\n- Putting a hash in front of x turns it into a string in our macro\n- __FILE__ gives the file location, not specific to a compiler\n- __LINE__ give the line for the code, not specific to a compiler\n\nNow for every GL call we make, we should wrap it with our macro for debugging\nPutting a scope in the macro definition might mess up stack memory variables\nthat are assigned to the output of a gl function\nexample:\n```\nGLCall(unsigned int program = glCreateProgram();)\n```\nIf we used a scope {} instead of \\ to create a new line, the program variable\nwill become undone due to the scope.\n\n## Uniforms\nAre a way to send data from the CPU to the shader. In our case, C++ to our shaders to use as a variable.\nLet's say we wanted to change the color of our square (2 triangles). We can do two things.\n- Use a uniform\n- send the data as an attribute with the vertex\n\n\nUniforms are set per draw, while attributes are set per vertex. We set the Uniform before the draw call.\nOne naming convention we can use for uniform variables are writing \"u_\" in front to signify a uniform\nHere is an example of passing a uniform to our Fragment Shader\n\nOur fragment shader before the uniform\n```\n#shader fragment\n#version 330 core\n        \nlayout(location = 0) out vec4 color;\n        \nvoid main()\n{\n    color = vec4(1.0, 0.0, 0.0, 1.0);\n};\n```\n\nOur fragment shader after the uniform\n```\n#shader fragment\n#version 330 core\n        \nlayout(location = 0) out vec4 color;\n\nuniform vec4 u_Color;\n        \nvoid main()\n{\n    color = u_Color;\n};\n```\n\nNow we set the variable from c++\n\n- since we are using vec4, we need 4 floats, thus we use glUniform4f\nonce a shader gets created, every shader gets an id so that we can reference it\nthe way we can look up the id, typically, is by its name.\n\n- glUniform4F's first parameter is the id for the uniform in the shader, which we\ncan get with glGetUniformLocation passing shader and the name of the uniform as arguments\n\n- We use ASSERT to make use location actually does exist, or create a breakpoint while debugging\n\n```\nunsigned int shader = CreateShader(source.VertexSource,source.FragmentSource);\nGLCall(glUseProgram(shader));\n\n\nGLCall(int location = glGetUniformLocation(shader, \"u_Color\"));\nASSERT(location != -1);\nGLCall(glUniform4f(location,0.2f,0.3f,0.8f,1.0f));\n```\n\nTo make our uniform value change, we can set a base value and increment value\noutside the render loop as such\n```\n    float r = 0.0f;\n    float increment = 0.05f;\n    while (!glfwWindowShouldClose(window))\n    {\n    ...\n    }\n```\n\nNow, within the loop, we set the uniform value again per frame using the variable r.\nOnce r is greater than 1.0f, the increment decreases, once it reaches zero, it increases.\nwe use r+=increment to change the value per frame.\n```\n    GLCall(glUniform4f(location, r, 0.3f, 0.8f, 1.0f));\n    GLCall(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr));\n\n    if (r \u003e 1.0f)\n        increment = -0.05f;\n    else if (r \u003c 0.0f)\n        increment = 0.05f;\n\n    r += increment;\n```\n\nTo limit our framerate to be less flashy, after the window context is created,\nwe use the function glfwSwapInterval() function and pass in a 1. This syncs the framerate\nwith our monitors refresh rate. The animation appears much smoother.\n\n```\nint main()\n{\n    GLFWwindow* window;\n    /* Initialize the library */\n    if (!glfwInit())\n        return -1;\n\n    /* Create a windowed mode window and its OpenGL context */\n    window = glfwCreateWindow(640, 480, \"Hello World\", NULL, NULL);\n    if (!window)\n    {\n        glfwTerminate();\n        return -1;\n    }\n\n    /* Make the window's context current */\n    glfwMakeContextCurrent(window);\n    glfwSwapInterval(1);\n\n    .\n    .\n    .\n\n    return 0;\n}\n```\n\nOne thing to keep in mind is that Uniforms are done on a per draw basis.\n\n\n\n## Vertex Arrays\nWhat is the difference between a vertex buffer and a vertex array?\nVertex Array doesn't exist in some other rendering apis.\nThey are a way to bind vertex buffers with a certain kind of specification/layout\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprogrammersteve%2Fopenglpractice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprogrammersteve%2Fopenglpractice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprogrammersteve%2Fopenglpractice/lists"}