shader是最终跑在GPU上的代码,执行真正的渲染逻辑,与Shader息息相关的概念有
glsl代码的编译/链接
向glsl传attribute变量
向glsl传uniform变量
glsl也是一门语言,每个版本之间会有语法特性,因为mac和linux用的OpenGL版本不一样,所以glsl的版本特性也不一样,所以shader的设计要支持运行时动态兼容
1 运行时OpenGL版本 1.1 要把采集到的版本信息保存起来 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct GLRendererInfo { int MajorVersion = 3 ; int MinorVersion = 3 ; bool ARB_gl_spirv = false ; bool IsAtLeast (int major, int minor) const { return MajorVersion > major || (MajorVersion == major && MinorVersion >= minor); } std::string GetGLSLVersionString () const { return "#version " + std::to_string (MajorVersion) + std::to_string (MinorVersion) + "0 core" ; } static GLRendererInfo& Get () ; };
1.2 OpenGLContext中拿到版本信息 GLFW负责初始化窗体,也会创建好OpenGL的Context,从里面可以拿到OpenGL的运行时版本
1 2 3 4 5 6 7 8 9 10 11 12 int versionMajor;int versionMinor;glGetIntegerv (GL_MAJOR_VERSION, &versionMajor);glGetIntegerv (GL_MINOR_VERSION, &versionMinor);X_CORE_ASSERT (versionMajor > 3 || (versionMajor == 3 && versionMinor >= 3 ), "Requires at least OpenGL version 3.3, not support {}.{}" , versionMajor, versionMinor);auto & info = X::GLRendererInfo::Get (); info.MajorVersion = versionMajor; info.MinorVersion = versionMinor;
2 shader类型区分 1 2 3 4 5 6 7 8 9 10 11 12 13 14 static GLenum shaderTypeFromString (const std::string& type) { if (type == "vertex" ) { return GL_VERTEX_SHADER; } if (type == "fragment" || type == "pixel" ) { return GL_FRAGMENT_SHADER; } X_CORE_ASSERT (false , "Unknown shader type" ); return 0 ; }
3 spriv字节码 跟教程里面不一样的是,当然首先是兼容了教程里面的方式。在高版本的OpenGL支持Spriv扩展支持,所以我增加了用Spriv链接的方式
1 2 3 4 5 6 7 8 9 for (auto && [stage, spirv] : m_spirvBinaries) { GLuint shaderID = shaderIDs.emplace_back (glCreateShader (stage)); glShaderBinary (1 , &shaderID, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, spirv.data (), spirv.size () * sizeof (uint32_t )); glSpecializeShaderARB (shaderID, "main" , 0 , nullptr , nullptr ); glAttachShader (program, shaderID); }
4 attribute变量怎么传 这个要结合VBO+VAO一起
用的核心API是glUniformxx
1 2 3 4 5 6 7 void OpenGLShader::uploadUniformInt (const std::string& name, int value) const { GLint location = glGetUniformLocation (m_rendererId, name.c_str ()); glUniform1i (location, value); }
6 UBO 这个生命周期虽然不是直接由shader,但是UBO本质就是服务shader的,上面的uniform是给每个shader单独用的,这样的局限可能造成大量的显存浪费。 因此UBO就是显存上的公共空间,让所有shader都可以共享。
OpenGL-0x06-UBO