{"id":711,"date":"2021-12-08T21:42:55","date_gmt":"2021-12-08T21:42:55","guid":{"rendered":"https:\/\/writingagame.com\/?p=711"},"modified":"2021-12-08T21:58:15","modified_gmt":"2021-12-08T21:58:15","slug":"chapter-23-ifdef-in-shaders","status":"publish","type":"post","link":"https:\/\/writingagame.com\/index.php\/2021\/12\/08\/chapter-23-ifdef-in-shaders\/","title":{"rendered":"Chapter 23. #ifdef in shaders"},"content":{"rendered":"\n<p>Right now we have 6 shaders (3 vertex+fragment pairs), which generate 3 shader-programs. Now we need to add textured Phong shader, then we&#8217;ll need color or texture over transparency mask for both <em>flat <\/em>and <em>phong <\/em>cases, which will double number of shaders, and so on, and soon it will be hard to manage them all. Besides, in most cases it will be just different combinations of the same code fragments.<\/p>\n\n\n\n<p>However, turned out that GLSL (shaders language) understands <strong>#define<\/strong> and <strong>#ifdef<\/strong> instructions, which allows to cover ALL mentioned cases by ONE single shaders pair.<\/p>\n\n\n\n<p>We can generate #define part programmatically, and then concatenate it with shader&#8217;s source code, which will let us generate multiple shader programs for different data sources (such as <em>uColor <\/em>or <em>texture <\/em>or else).<\/p>\n\n\n\n<p>1. In <strong>Windows File Explorer<\/strong> open <em>C:\\CPP\\engine\\dt\\shaders<\/em> folder.<\/p>\n\n\n\n<p><strong>Delete <\/strong>all 6 files from there<\/p>\n\n\n\n<p>and replace them by:<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>2. <strong>Vertex shader<\/strong>:<\/p>\n\n\n\n<p>Copy following code in a <strong>Text Editor<\/strong> and save it as a txt file in<\/p>\n\n\n\n<p><em>C:\\CPP\\engine\\dt\\shaders\\phong_<strong>v<\/strong>.txt<\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/#version 320 es\nprecision lowp float;\nuniform mat4 uMVP; \/\/ transform matrix (Model-View-Projection)\nuniform mat3 uMV3x3; \/\/ Model-View matrix (for calculating normals into eye space)\n\nin vec3 aPos; \/\/ position attribute (x,y,z)\n#if defined(USE_NORMALS)\n\tin vec3 aNormal; \/\/ normal attribute (x,y,z)\n\tout vec3 vNormal; \/\/ varying normal (to pass to fragment shader)\n#endif\n#if defined(USE_TUV0)\n\tin vec2 aTuv; \/\/attribute TUV (texture coordinates)\n#endif\n#if defined(USE_TEX0)\n\tout vec2 vTuv; \/\/varying TUV (pass to fragment shader)\n#endif\n\nvoid main(void) { \n\tgl_Position = uMVP * vec4(aPos, 1.0);\n#if defined(USE_NORMALS)\t\n\t\/\/ Transform the normal&#039;s orientation into eye space. \n\tvNormal = uMV3x3 * aNormal;\n#endif\n#if defined(USE_TUV0)\n\tvTuv = aTuv;\n#endif\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>3. <strong>Fragment shader<\/strong>:<\/p>\n\n\n\n<p>Copy following code in a <strong>Text Editor<\/strong> and save it as a txt file in<\/p>\n\n\n\n<p><em>C:\\CPP\\engine\\dt\\shaders\\phong_<strong>f<\/strong>.txt<\/em><\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\/\/#version 320 es\nprecision lowp float;\nout vec4 FragColor; \/\/output pixel color\nuniform float uAlphaFactor; \/\/for semi-transparency\n\n#if defined(USE_NORMALS)\n\tin vec3 vNormal; \/\/normal passed from rasterizer\n#endif\n#if defined(USE_TEX0)\n\tuniform sampler2D uTex0;  \/\/texture id\n\tin vec2 vTuv; \/\/varying TUV (passed from vertex shader)\n#else\n\tuniform vec4 uColor;\n#endif\n\n#if defined(PHONG)\n\tuniform float uAmbient;\n\tuniform float uSpecularIntencity;\n\tuniform float uSpecularMinDot;\n\tuniform float uSpecularPowerOf;\n\n\tuniform vec3 uVectorToLight;\n\tuniform vec3 uHalfVector;\n#endif\n\nvoid main(void) {\n\n#if defined(USE_TEX0)\n\tFragColor = texture(uTex0, vTuv);\n#else\n\tFragColor = uColor;\n#endif\n\n#if defined(USE_NORMALS)\n\tvec3 vNormalNormal = normalize(vNormal);\n#endif\n\n#if defined(PHONG)\n\tif(uAmbient&lt;1.0){\n\t\t \/\/ Calculate the dot product of the light vector and vertex normal. If the normal and light vector are\n\t\t \/\/ pointing in the same direction then it will get max illumination.\n\t\t float directionalLightIntencity = dot(vNormalNormal, uVectorToLight);\n\t\t\n\t\t \/\/ count ambient component\n\t\t directionalLightIntencity += uAmbient;\n\t\t if(directionalLightIntencity &lt; uAmbient)\n\t\t\tdirectionalLightIntencity = uAmbient;\n\n\t\t \/\/ Multiply the color by the lightIntencity illumination level to get final output color.\n\t\t FragColor *= directionalLightIntencity;\n\t}\n\tif(uSpecularIntencity&gt;0.0){\n\t\t\/\/specular light\n\t\t\/\/ INTENSITY OF THE SPECULAR LIGHT\n\t\t\/\/ DOT PRODUCT OF NORMAL VECTOR AND THE HALF VECTOR TO THE POWER OF THE SPECULAR HARDNESS\n\t\tfloat dotProduct = dot(vNormalNormal, uHalfVector);\n\n\t\tif(dotProduct&gt;uSpecularMinDot){\n\t\t\tfloat specularIntencity = pow(dotProduct, uSpecularPowerOf) * uSpecularIntencity;\t\t\n\t\t\tif(specularIntencity &gt; uSpecularIntencity)\n\t\t\t\tspecularIntencity = uSpecularIntencity;\n\t\t\tFragColor += specularIntencity;\n\t\t}\n\t}\n#endif\n\tif(uAlphaFactor != 1.0)\n\t\tFragColor.a *= uAlphaFactor;\t\t\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>Now we need to load them in the memory, concatenate with <strong>#deines<\/strong> string, translate and link into executable shader programs.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">File Loader<\/h2>\n\n\n\n<p>Reading\/loading files into the memory for subsequent manipulations\/processing is a quite common task. We did it before and will a lot in the future. Makes sense to wrap it in a separate class. We&#8217;ll call it <strong>FileLoader<\/strong>.<\/p>\n\n\n\n<p>4. Start VS, open&nbsp;<em>C:\\CPP\\<em>a997modeler<\/em>\\p_windows\\p_windows.sln<\/em><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>5. Under <em>xEngine <\/em>add new header file <strong>FileLoader.h<\/strong><\/p>\n\n\n\n<p>Location: C:\\CPP\\engine<\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#pragma once\n#include &lt;string&gt;\n\nclass FileLoader\n{\npublic:\n\tstd::string fullPath;\n\tstd::string inAppFolder;\n\tchar* pData = NULL;\n\tint dataSize = 0;\npublic:\n\tFileLoader(std::string filePath, std::string readMode=&quot;rt&quot;);\n\tvirtual ~FileLoader();\n\tstatic int translatePath(FileLoader* pFL, std::string filePath);\n\tstatic int loadFile(FileLoader* pFL, std::string filePath, std::string readMode=&quot;rb&quot;);\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><em>translatePath()<\/em> function will check <em>filePath <\/em>parameter and will concatenate it with <em>filesRoot <\/em>when necessary. Also it will extract <em>inAppFolder <\/em>for future use.<\/li><li><em>loadFile()<\/em> will retrieve file&#8217;s size, allocate memory and load file into the memory.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p><strong>Implementation<\/strong>:<\/p>\n\n\n\n<p>6. Under <em>xEngine <\/em>add new C++ file <strong>FileLoader.cpp<\/strong><\/p>\n\n\n\n<p>Location: <em>C:\\CPP\\engine<\/em><\/p>\n\n\n\n<p>Code:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &quot;FileLoader.h&quot;\n#include &quot;platform.h&quot;\n\nextern std::string filesRoot;\n\nFileLoader::FileLoader(std::string filePath, std::string readMode) {\n    loadFile(this, filePath, readMode);\n}\nFileLoader::~FileLoader() {\n    if (pData != NULL) {\n        delete&#91;] pData;\n        pData = NULL;\n    }\n}\nint FileLoader::translatePath(FileLoader* pFL, std::string filePath) {\n    if (filePath.find(filesRoot) == 0)\n        pFL-&gt;fullPath = filePath;\n    else\n        pFL-&gt;fullPath = filesRoot + filePath;\n    int startPos = filesRoot.size();\n    int lastSlashPos = pFL-&gt;fullPath.find_last_of(&#039;\/&#039;);\n    pFL-&gt;inAppFolder = pFL-&gt;fullPath.substr(startPos, lastSlashPos - startPos + 1);\n    return 1;\n}\nint FileLoader::loadFile(FileLoader* pFL, std::string filePath, std::string readMode) {\n    translatePath(pFL, filePath);\n    FILE* pFile;\n    myFopen_s(&amp;pFile, pFL-&gt;fullPath.c_str(), readMode.c_str());\n    if (pFile != NULL)\n    {\n        \/\/ obtain file size:\n        fseek(pFile, 0, SEEK_END);\n        pFL-&gt;dataSize = ftell(pFile);\n        rewind(pFile);\n        \/\/ size obtained, create buffer\n        pFL-&gt;pData = new char&#91;pFL-&gt;dataSize + 1];\n        pFL-&gt;dataSize = fread(pFL-&gt;pData, 1, pFL-&gt;dataSize, pFile);\n        pFL-&gt;pData&#91;pFL-&gt;dataSize] = 0;\n        fclose(pFile);\n    }\n    else {\n        mylog(&quot;ERROR loading %s\\n&quot;, pFL-&gt;fullPath.c_str());\n        return -1;\n    }\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>When shaders are loaded into the memory, we need to set &#8220;<strong>defines<\/strong>&#8221; part. Now, for shaders compilation we will use strings ARRAY, consisting of 2 strings:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>&#8220;defines&#8221; string, which we will set programmatically<\/li><li>and shader&#8217;s source code, which actually is a &#8220;string&#8221; too.<\/li><\/ul>\n\n\n\n<p>We will do it in <strong>Shader <\/strong>class.<\/p>\n\n\n\n<p>7. Open <em>Shader.h<\/em> and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [63,64,65]; title: ; notranslate\" title=\"\">\n#pragma once\n#include &quot;platform.h&quot;\n#include &lt;string&gt;\n#include &lt;vector&gt;\n\nclass Shader\n{\npublic:\n    \/\/Shader program&#039;s individual descriptor:\n    unsigned int GLid = -1; \/\/ GL shader id\n    \/\/common variables, &quot;l_&quot; for &quot;location&quot;\n    \/\/attributes\n    int l_aPos; \/\/attribute position (3D coordinates)\n    int l_aTuv; \/\/attribute TUV (texture coordinates)\n    int l_aTuv2; \/\/attribute TUV (texture coordinates for normal map)\n    int l_aNormal; \/\/attribute normal (3D vector)\n    int l_aTangent; \/\/for normal map\n    int l_aBinormal; \/\/for normal map\n    \/\/uniforms\n    int l_uMVP; \/\/ transform matrix (Model-View-Projection)\n    int l_uMV3x3; \/\/ Model-View matrix for normals\n    int l_uVectorToLight; \/\/required for light\n    int l_uHalfVector; \/\/required for specular light\n    \/\/material&#039;s properties\n    int l_uColor;\n    int l_uTex0; \/\/texture id\n    int l_uTex1mask; \/\/transparency map\n    int l_uTex2nm; \/\/normal map\n    int l_uTex3; \/\/texture id\n    int l_uTex1alphaChannelN; \/\/alpha channel for mask\n    int l_uTex1alphaNegative; \/\/alpha channel negative\n    int l_uTex0translateChannelN; \/\/translate tex0 to tex3 by channelN.\n    int l_uAlphaFactor; \/\/for semi-transparency\n    int l_uAlphaBlending; \/\/for semi-transparency\n    \/\/light:\n    int l_uAmbient; \/\/ambient light\n    \/\/specular light parameters\n    int l_uSpecularIntencity;\n    int l_uSpecularMinDot;\n    int l_uSpecularPowerOf;\n    \/\/end of descriptor\n\n    \/\/static array (vector) of all loaded shaders\n    static std::vector&lt;Shader*&gt; shaders;\n    \/\/common shader programs (&quot;spN&quot; - shader program number)\n    static int spN_flat_ucolor;\n    static int spN_flat_tex;\n    static int spN_phong_ucolor;\n    static int spN_phong_tex;\n\npublic:\n    static int loadShaders();\n    static int cleanUp();\n    static unsigned int getGLid(int shN) { return shaders.at(shN)-&gt;GLid; };\n    static int shaderErrorCheck(int shaderId, std::string ref);\n    static int programErrorCheck(int programId, std::string ref);\n    static int fillLocations(Shader* pSh);\n\n    static int buildShaderObjectFromFiles(std::string filePathVertexS, std::string filePathFragmentS);\n    static int linkShaderProgramFromFiles(const char* filePathVertexS, const char* filePathFragmentS);\n\tstatic int compileShaderFromFile(const char* filePath, GLenum shaderType);\n\n    static int buildShaderObjectWithDefines(std::string definesString, char* sourceVertex, char* sourceFragment);\n    static int linkShaderProgramWithDefines(std::string definesString, char* sourceVertex, char* sourceFragment);\n    static int compileShaderWithDefines(std::string definesString, char* shaderSource, GLenum shaderType);\n\n};\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<p>We have 3 new functions here:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><em>compileShaderWithDefines<\/em>, which accepts &#8220;def&#8221;-string and 1 shader source code as parameters, and then compiles vertex or fragment shader, which will be linked together in<\/li><li><em>linkShaderProgramWithDefines<\/em>, which calls 2 compilations and links 2 compiled shaders into a Shader Program, which will be returned to<\/li><li><em>buildShaderObjectWithDefines<\/em>, which gets a &#8220;def&#8221;-string and 2 source codes as parameters, extends defString, calls <em>linkShaderProgramWithDefines<\/em>, analyses linked shader program, fills out variables locations and saves this info, including GL Program&#8217;s ID in a new Shader object.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>8.  Open <em>Shader.cpp<\/em> and replace code by: <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [14,16,151,162,207]; title: ; notranslate\" title=\"\">\n#include &quot;Shader.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;FileLoader.h&quot;\n\nextern std::string filesRoot;\n\n\/\/static array (vector) of all loaded shaders\nstd::vector&lt;Shader*&gt; Shader::shaders;\n\/\/common shader programs (&quot;spN&quot; - shader program number)\nint Shader::spN_flat_ucolor = -1;\nint Shader::spN_flat_tex = -1;\nint Shader::spN_phong_ucolor = -1;\nint Shader::spN_phong_tex = -1;\n\nint Shader::loadShaders() {\n    FileLoader* pFLvertex = new FileLoader(&quot;\/dt\/shaders\/phong_v.txt&quot;);\n    FileLoader* pFLfragment = new FileLoader(&quot;\/dt\/shaders\/phong_f.txt&quot;);\n    spN_flat_ucolor = buildShaderObjectWithDefines(&quot;#define FLAT\\n#define COLOR\\n&quot;, pFLvertex-&gt;pData, pFLfragment-&gt;pData);\n    spN_flat_tex = buildShaderObjectWithDefines(&quot;#define FLAT\\n#define TEXTURE\\n&quot;, pFLvertex-&gt;pData, pFLfragment-&gt;pData);\n    spN_phong_ucolor = buildShaderObjectWithDefines(&quot;#define PHONG\\n#define COLOR\\n&quot;, pFLvertex-&gt;pData, pFLfragment-&gt;pData);\n    spN_phong_tex = buildShaderObjectWithDefines(&quot;#define PHONG\\n#define TEXTURE\\n&quot;, pFLvertex-&gt;pData, pFLfragment-&gt;pData);\n    delete pFLvertex;\n    delete pFLfragment;\n    return 1;\n}\nint Shader::buildShaderObjectFromFiles(std::string filePathVertexS, std::string filePathFragmentS) {\n    \/\/create shader object\n    Shader* pSh = new Shader();\n    shaders.push_back(pSh);\n    pSh-&gt;GLid = linkShaderProgramFromFiles((filesRoot + filePathVertexS).c_str(), (filesRoot + filePathFragmentS).c_str());\n    \/\/common variables. If not presented, = -1;\n    fillLocations(pSh);\n\n    return (shaders.size() - 1);\n}\nint Shader::fillLocations(Shader* pSh) {\n    \/\/common variables. If not presented, = -1;\n    \/\/attributes\n    pSh-&gt;l_aPos = glGetAttribLocation(pSh-&gt;GLid, &quot;aPos&quot;); \/\/attribute position (3D coordinates)\n    pSh-&gt;l_aNormal = glGetAttribLocation(pSh-&gt;GLid, &quot;aNormal&quot;); \/\/attribute normal (3D vector)\n    pSh-&gt;l_aTangent = glGetAttribLocation(pSh-&gt;GLid, &quot;aTangent&quot;); \/\/for normal map\n    pSh-&gt;l_aBinormal = glGetAttribLocation(pSh-&gt;GLid, &quot;aBinormal&quot;); \/\/for normal map\n    pSh-&gt;l_aTuv = glGetAttribLocation(pSh-&gt;GLid, &quot;aTuv&quot;); \/\/attribute TUV (texture coordinates)\n    pSh-&gt;l_aTuv2 = glGetAttribLocation(pSh-&gt;GLid, &quot;aTuv2&quot;); \/\/attribute TUV (texture coordinates)\n    \/\/uniforms\n    pSh-&gt;l_uMVP = glGetUniformLocation(pSh-&gt;GLid, &quot;uMVP&quot;); \/\/ transform matrix (Model-View-Projection)\n    pSh-&gt;l_uMV3x3 = glGetUniformLocation(pSh-&gt;GLid, &quot;uMV3x3&quot;); \/\/ Model-View matrix for normals\n    pSh-&gt;l_uVectorToLight = glGetUniformLocation(pSh-&gt;GLid, &quot;uVectorToLight&quot;); \/\/ \n    pSh-&gt;l_uHalfVector = glGetUniformLocation(pSh-&gt;GLid, &quot;uHalfVector&quot;); \/\/ required for specular light\n    \/\/material&#039;s properties\n    pSh-&gt;l_uColor = glGetUniformLocation(pSh-&gt;GLid, &quot;uColor&quot;);\n    pSh-&gt;l_uTex0 = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex0&quot;); \/\/texture id\n    pSh-&gt;l_uTex1mask = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex1mask&quot;); \/\/texture id\n    pSh-&gt;l_uTex2nm = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex2nm&quot;); \/\/texture id\n    pSh-&gt;l_uTex3 = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex3&quot;); \/\/texture id\n    pSh-&gt;l_uTex1alphaChannelN = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex1alphaChannelN&quot;);\n    pSh-&gt;l_uTex1alphaNegative = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex1alphaNegative&quot;);\n    pSh-&gt;l_uTex0translateChannelN = glGetUniformLocation(pSh-&gt;GLid, &quot;uTex0translateChannelN&quot;);\n    pSh-&gt;l_uAlphaFactor = glGetUniformLocation(pSh-&gt;GLid, &quot;uAlphaFactor&quot;); \/\/ for semi-transparency\n    pSh-&gt;l_uAlphaBlending = glGetUniformLocation(pSh-&gt;GLid, &quot;uAlphaBlending&quot;); \/\/ for semi-transparency\n    pSh-&gt;l_uAmbient = glGetUniformLocation(pSh-&gt;GLid, &quot;uAmbient&quot;); \/\/ ambient light\n    pSh-&gt;l_uSpecularIntencity = glGetUniformLocation(pSh-&gt;GLid, &quot;uSpecularIntencity&quot;); \/\/ \n    pSh-&gt;l_uSpecularMinDot = glGetUniformLocation(pSh-&gt;GLid, &quot;uSpecularMinDot&quot;); \/\/ \n    pSh-&gt;l_uSpecularPowerOf = glGetUniformLocation(pSh-&gt;GLid, &quot;uSpecularPowerOf&quot;); \/\/ \n    return 1;\n}\nint Shader::cleanUp() {\n    int shadersN = shaders.size();\n    if (shadersN &lt; 1)\n        return -1;\n    glUseProgram(0);\n    for (int i = 0; i &lt; shadersN; i++) {\n        Shader* pSh = shaders.at(i);\n        glDeleteProgram(pSh-&gt;GLid);\n        delete pSh;\n    }\n    shaders.clear();\n    return 1;\n}\n\nGLchar infoLog&#91;1024];\nint logLength;\nint Shader::shaderErrorCheck(int shaderId, std::string ref) {\n    \/\/use after glCompileShader()\n    if (checkGLerrors(ref) &gt; 0)\n        return -1;\n    glGetShaderInfoLog(shaderId, 1024, &amp;logLength, infoLog);\n    if (logLength == 0)\n        return 0;\n    mylog(&quot;%s shader infoLog:\\n%s\\n&quot;, ref.c_str(), infoLog);\n    return -1;\n}\nint Shader::programErrorCheck(int programId, std::string ref) {\n    \/\/use after glLinkProgram()\n    if (checkGLerrors(ref) &gt; 0)\n        return -1;\n    glGetProgramInfoLog(programId, 1024, &amp;logLength, infoLog);\n    if (logLength == 0)\n        return 0;\n    mylog(&quot;%s program infoLog:\\n%s\\n&quot;, ref.c_str(), infoLog);\n    return -1;\n}\n\nint Shader::compileShaderFromFile(const char* filePath, GLenum shaderType) {\n    int shaderId = glCreateShader(shaderType);\n    FILE* pFile;\n    myFopen_s(&amp;pFile, filePath, &quot;rt&quot;);\n    if (pFile != NULL)\n    {\n        \/\/ obtain file size:\n        fseek(pFile, 0, SEEK_END);\n        int fSize = ftell(pFile);\n        rewind(pFile);\n        \/\/ size obtained, create buffer\n        char* shaderSource = new char&#91;fSize + 1];\n        fSize = fread(shaderSource, 1, fSize, pFile);\n        shaderSource&#91;fSize] = 0;\n        fclose(pFile);\n        \/\/ source code loaded, compile\n        glShaderSource(shaderId, 1, (const GLchar**)&amp;shaderSource, NULL);\n        \/\/myglErrorCheck(&quot;glShaderSource&quot;);\n        glCompileShader(shaderId);\n        if (shaderErrorCheck(shaderId, &quot;glCompileShader&quot;) &lt; 0)\n            return -1;\n        delete&#91;] shaderSource;\n    }\n    else {\n        mylog(&quot;ERROR loading %s\\n&quot;, filePath);\n        return -1;\n    }\n    return shaderId;\n}\nint Shader::linkShaderProgramFromFiles(const char* filePathVertexS, const char* filePathFragmentS) {\n    int vertexShaderId = compileShaderFromFile(filePathVertexS, GL_VERTEX_SHADER);\n    int fragmentShaderId = compileShaderFromFile(filePathFragmentS, GL_FRAGMENT_SHADER);\n    int programId = glCreateProgram();\n    glAttachShader(programId, vertexShaderId);\n    glAttachShader(programId, fragmentShaderId);\n    glLinkProgram(programId);\n    if (programErrorCheck(programId, &quot;glLinkProgram&quot;) &lt; 0)\n        return -1;\n    \/\/don&#039;t need shaders any longer - detach and delete them\n    glDetachShader(programId, vertexShaderId);\n    glDetachShader(programId, fragmentShaderId);\n    glDeleteShader(vertexShaderId);\n    glDeleteShader(fragmentShaderId);\n    return programId;\n}\n\nint Shader::buildShaderObjectWithDefines(std::string definesString, char* sourceVertex, char* sourceFragment) {\n    \/\/create shader object\n    Shader* pSh = new Shader();\n    shaders.push_back(pSh);\n\n    pSh-&gt;GLid = linkShaderProgramWithDefines(definesString, sourceVertex, sourceFragment);\n    \/\/common variables. If not presented, = -1;\n    fillLocations(pSh);\n\n    return (shaders.size() - 1);\n}\nint Shader::linkShaderProgramWithDefines(std::string definesString00, char* sourceVertex, char* sourceFragment) {\n    \/\/build extended definesString\n    bool bUSE_NORMALS = false;\n    bool bUSE_TEX0 = false;\n    bool bUSE_TUV0 = false;\n    if (definesString00.find(&quot; PHONG\\n&quot;) != std::string::npos)\n        bUSE_NORMALS = true;\n    if (definesString00.find(&quot; TEXTURE\\n&quot;) != std::string::npos) {\n        bUSE_TEX0 = true;\n        bUSE_TUV0 = true;\n    }\n    if (definesString00.find(&quot; MIRROR\\n&quot;) != std::string::npos) {\n        bUSE_NORMALS = true;\n        bUSE_TEX0 = true;\n    }\n    if (definesString00.find(&quot; OVERMASK\\n&quot;) != std::string::npos) {\n        bUSE_TUV0 = true;\n    }\n    std::string definesString;\n    definesString.assign(&quot;#version 320 es\\n&quot;);\n    definesString.append(definesString00);\n    if (bUSE_NORMALS)\n        definesString.append(&quot;#define USE_NORMALS\\n&quot;);\n    if (bUSE_TEX0)\n        definesString.append(&quot;#define USE_TEX0\\n&quot;);\n    if (bUSE_TUV0)\n        definesString.append(&quot;#define USE_TUV0\\n&quot;);\n\n    int vertexShaderId = compileShaderWithDefines(definesString, sourceVertex, GL_VERTEX_SHADER);\n    int fragmentShaderId = compileShaderWithDefines(definesString, sourceFragment, GL_FRAGMENT_SHADER);\n\n    int programId = glCreateProgram();\n    glAttachShader(programId, vertexShaderId);\n    glAttachShader(programId, fragmentShaderId);\n    glLinkProgram(programId);\n    if (programErrorCheck(programId, &quot;glLinkProgram&quot;) &lt; 0)\n        return -1;\n    \/\/don&#039;t need shaders any longer - detach and delete them\n    glDetachShader(programId, vertexShaderId);\n    glDetachShader(programId, fragmentShaderId);\n    glDeleteShader(vertexShaderId);\n    glDeleteShader(fragmentShaderId);\n    \/\/mylog(&quot;linking program\\n%s\\n&quot;, definesString.c_str());\n    return programId;\n}\nint Shader::compileShaderWithDefines(std::string definesString, char* shaderSource, GLenum shaderType) {\n    int shaderId = glCreateShader(shaderType);\n    if (definesString.empty())\n        glShaderSource(shaderId, 1, (const GLchar**)&amp;shaderSource, NULL);\n    else { \/\/2 strings\n        const char* sourceStrings&#91;2];\n        sourceStrings&#91;0] = definesString.c_str();\n        sourceStrings&#91;1] = shaderSource;\n        \/\/ source code loaded, compile\n        glShaderSource(shaderId, 2, (const GLchar**)sourceStrings, NULL);\n    }\n    \/\/myglErrorCheck(&quot;glShaderSource&quot;);\n    glCompileShader(shaderId);\n    if (shaderErrorCheck(shaderId, &quot;glCompileShader&quot;) &lt; 0) {\n        mylog(&quot;ERROR in compileShader,\\n%s\\n%s\\n&quot;, definesString.c_str(), shaderSource);\n        return -1;\n    }\n    return shaderId;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Now <em>Shader::loadShaders()<\/em> has a different format. Plus now we are building not 3, but 4 shaders, including new Phong textured shader.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now in  <em>TheGame.cpp<\/em> we can set <strong>Phong <\/strong>shader for textured surface. Don&#8217;t have to flip it upside down any more.<\/p>\n\n\n\n<p>9. Open  <em>TheGame.cpp<\/em> and replace code by:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; highlight: [59,62]; title: ; notranslate\" title=\"\">\n#include &quot;TheGame.h&quot;\n#include &quot;platform.h&quot;\n#include &quot;utils.h&quot;\n#include &quot;linmath.h&quot;\n#include &quot;Texture.h&quot;\n#include &quot;Shader.h&quot;\n#include &quot;DrawJob.h&quot;\n#include &quot;ModelBuilder.h&quot;\n#include &quot;TexCoords.h&quot;\n\nextern std::string filesRoot;\nextern float degrees2radians;\n\nstd::vector&lt;GameSubj*&gt; TheGame::gameSubjs;\n\nint TheGame::getReady() {\n    bExitGame = false;\n    Shader::loadShaders();\n    glEnable(GL_CULL_FACE);\n\n    \/\/=== create box ========================\n    GameSubj* pGS = new GameSubj();\n    gameSubjs.push_back(pGS);\n\n    pGS-&gt;name.assign(&quot;box1&quot;);\n    pGS-&gt;ownCoords.setPosition(0, 0, 0);\n    pGS-&gt;ownCoords.setDegrees(0, 0, 0);\n    pGS-&gt;ownSpeed.setDegrees(0,1,0);\n\n    ModelBuilder* pMB = new ModelBuilder();\n    pMB-&gt;useSubjN(gameSubjs.size() - 1);\n\n    \/\/define VirtualShape\n    VirtualShape vs;\n    vs.setShapeType(&quot;box-tank&quot;);\n    vs.whl&#91;0] = 60;\n    vs.whl&#91;1] = 160;\n    vs.whl&#91;2] = 390;\n    vs.setExt(20);\n    vs.extD = 0;\n    vs.extF = 0; \/\/to make front face &quot;flat&quot;\n    vs.sectionsR = 2;\n\n    Material mt;\n    \/\/define material - flat red\n    mt.shaderN = Shader::spN_phong_ucolor;\n    mt.primitiveType = GL_TRIANGLES;\n    mt.uColor.setRGBA(255, 0, 0,255); \/\/red\n    pMB-&gt;useMaterial(&amp;mt);\n\n    pMB-&gt;buildBoxFace(pMB,&quot;front v&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;back v&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;top&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;bottom&quot;, &amp;vs);\n    pMB-&gt;buildBoxFace(pMB, &quot;left all&quot;, &amp;vs);\n\n    mt.uColor.clear(); \/\/ set to zero;\n    mt.uTex0 = Texture::loadTexture(filesRoot + &quot;\/dt\/sample_img.png&quot;);\n    mt.shaderN = Shader::spN_phong_tex;\n    pMB-&gt;useMaterial(&amp;mt);\n    TexCoords tc;\n    tc.set(mt.uTex0, 11, 12, 256, 128, &quot;h&quot;); \/\/flip horizontally\n    pMB-&gt;buildBoxFace(pMB, &quot;right all&quot;, &amp;vs, &amp;tc);\n\n    pMB-&gt;buildDrawJobs(gameSubjs);\n\n    delete pMB;\n\n    \/\/===== set up camera\n    mainCamera.ownCoords.setDegrees(15, 180, 0); \/\/set camera angles\/orientation\n    mainCamera.viewRangeDg = 30;\n    mainCamera.stageSize&#91;0] = 500;\n    mainCamera.stageSize&#91;1] = 375;\n    memcpy(mainCamera.lookAtPoint, pGS-&gt;ownCoords.pos, sizeof(float) * 3);\n    mainCamera.onScreenResize();\n\n    \/\/===== set up light\n    v3set(dirToMainLight, -1, 1, 1);\n    vec3_norm(dirToMainLight, dirToMainLight);\n\n    return 1;\n}\nint TheGame::drawFrame() {\n    myPollEvents();\n\n    \/\/glClearColor(0.0, 0.0, 0.5, 1.0);\n    glClear(GL_COLOR_BUFFER_BIT);\n\n    \/\/calculate halfVector\n    float dirToCamera&#91;4] = { 0,0,-1,0 }; \/\/-z\n    mat4x4_mul_vec4plus(dirToCamera, *mainCamera.ownCoords.getRotationMatrix(), dirToCamera, 0);\n\n    float uHalfVector&#91;4] = { 0,0,0,0 };\n    for (int i = 0; i &lt; 3; i++)\n        uHalfVector&#91;i] = (dirToCamera&#91;i] + dirToMainLight&#91;i]) \/ 2;\n    vec3_norm(uHalfVector, uHalfVector);\n\n    mat4x4 mProjection, mViewProjection, mMVP, mMV4x4;\n    \/\/mat4x4_ortho(mProjection, -(float)screenSize&#91;0] \/ 2, (float)screenSize&#91;0] \/ 2, -(float)screenSize&#91;1] \/ 2, (float)screenSize&#91;1] \/ 2, 100.f, 500.f);\n    float nearClip = mainCamera.focusDistance - 250;\n    float farClip = mainCamera.focusDistance + 250;\n    mat4x4_perspective(mProjection, mainCamera.viewRangeDg * degrees2radians, screenAspectRatio, nearClip, farClip);\n    mat4x4_mul(mViewProjection, mProjection, mainCamera.lookAtMatrix);\n    \/\/mViewProjection&#91;1]&#91;3] = 0; \/\/keystone effect\n\n    \/\/scan subjects\n    int subjsN = gameSubjs.size();\n    for (int subjN = 0; subjN &lt; subjsN; subjN++) {\n        GameSubj* pGS = gameSubjs.at(subjN);\n        \/\/behavior - apply rotation speed\n        pGS-&gt;moveSubj();\n        \/\/prepare subject for rendering\n        pGS-&gt;buildModelMatrix(pGS);\n        \/\/build MVP matrix for given subject\n        mat4x4_mul(mMVP, mViewProjection, pGS-&gt;ownModelMatrix);\n        \/\/build Model-View (rotation) matrix for normals\n        mat4x4_mul(mMV4x4, mainCamera.lookAtMatrix, (vec4*)pGS-&gt;ownCoords.getRotationMatrix());\n        \/\/convert to 3x3 matrix\n        float mMV3x3&#91;3]&#91;3];\n        for (int y = 0; y &lt; 3; y++)\n            for (int x = 0; x &lt; 3; x++)\n                mMV3x3&#91;y]&#91;x] = mMV4x4&#91;y]&#91;x];\n        \/\/render subject\n        for (int i = 0; i &lt; pGS-&gt;djTotalN; i++) {\n            DrawJob* pDJ = DrawJob::drawJobs.at(pGS-&gt;djStartN + i);\n            pDJ-&gt;execute((float*)mMVP, *mMV3x3, dirToMainLight, uHalfVector, NULL);\n        }\n    }\n    mySwapBuffers();\n    return 1;\n}\n\nint TheGame::cleanUp() {\n    int itemsN = gameSubjs.size();\n    \/\/delete all UISubjs\n    for (int i = 0; i &lt; itemsN; i++) {\n        GameSubj* pGS = gameSubjs.at(i);\n        delete pGS;\n    }\n    gameSubjs.clear();\n    \/\/clear all other classes\n    Texture::cleanUp();\n    Shader::cleanUp();\n    DrawJob::cleanUp();\n    return 1;\n}\nint TheGame::onScreenResize(int width, int height) {\n    if (screenSize&#91;0] == width &amp;&amp; screenSize&#91;1] == height)\n        return 0;\n    screenSize&#91;0] = width;\n    screenSize&#91;1] = height;\n    screenAspectRatio = (float)width \/ height;\n    glViewport(0, 0, width, height);\n    mainCamera.onScreenResize();\n    mylog(&quot; screen size %d x %d\\n&quot;, width, height);\n    return 1;\n}\nint TheGame::run() {\n    getReady();\n    while (!bExitGame) {\n        drawFrame();\n    }\n    cleanUp();\n    return 1;\n}\n\n<\/pre><\/div>\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>10. Build and run.<\/p>\n\n\n\n<p><strong>Before<\/strong>:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c21\/00.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><em>After<\/em>:<\/p>\n\n\n\n<p><img decoding=\"async\" src=\"https:\/\/writingagame.com\/img\/b01\/c23\/00.jpg\"><\/p>\n\n\n\n<p><\/p>\n\n\n\n<iframe loading=\"lazy\" width=\"100%\" height=\"400\" src=\"https:\/\/www.youtube.com\/embed\/Zr2apqC7bQg?controls=0&amp;autoplay=1&amp;loop=1&amp;playlist=Zr2apqC7bQg\" title=\"Phong shader\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"\"><\/iframe>\n\n\n\n<p><\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>Now<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Android<\/h2>\n\n\n\n<p>11. Close and re-open VS. Open&nbsp;<em>C:\\CPP\\a997modeler\\p_android\\p_android.sln<\/em>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>12. Under <em>xEngine <\/em>add Existing Item<\/p>\n\n\n\n<p>from <em>C:\\CPP\\engine<\/em>:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>FileLoader.cpp<\/li><li>FileLoader.h<\/li><\/ul>\n\n\n\n<p><strong>Add<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p>13. Switch on, unlock, plug in, allow.<\/p>\n\n\n\n<p>Build and run.<\/p>\n\n\n\n<p>Good.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p class=\"mb-2\">Right now we have 6 shaders (3 vertex+fragment pairs), which generate 3 shader-programs. Now we need to add textured Phong shader, then we&#8217;ll need color or texture over transparency mask for both flat and phong cases, which will double number of shaders, and so on, and soon it will be hard to manage them all. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":725,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-711","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cross-platform-3d"],"_links":{"self":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/711","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/comments?post=711"}],"version-history":[{"count":23,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/711\/revisions"}],"predecessor-version":[{"id":743,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/posts\/711\/revisions\/743"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media\/725"}],"wp:attachment":[{"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/media?parent=711"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/categories?post=711"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/writingagame.com\/index.php\/wp-json\/wp\/v2\/tags?post=711"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}