{"id":465,"date":"2022-01-30T09:04:10","date_gmt":"2022-01-30T01:04:10","guid":{"rendered":"http:\/\/liyanliang.net\/?p=465"},"modified":"2022-01-30T09:04:52","modified_gmt":"2022-01-30T01:04:52","slug":"opengl-cartoonshading","status":"publish","type":"post","link":"http:\/\/liyanliang.net\/index.php\/2022\/01\/30\/opengl-cartoonshading\/","title":{"rendered":"OpenGL-\u5361\u901a\u7740\u8272(Cartoon)"},"content":{"rendered":"\n<p><strong>\u539f\u7406<\/strong><\/p>\n\n\n\n<p>\u8336\u58f6\u4e0a\u7684\u8272\u8c03\u901a\u8fc7\u89d2\u5ea6\u7684\u4f59\u5f26\u503c\u6765\u9009\u62e9\u7684\uff0c\u8fd9\u4e2a\u89d2\u5ea6\u662f\u6307\u5149\u7ebf\u548c\u9762\u7684\u6cd5\u7ebf\u4e4b\u95f4\u7684\u5939\u89d2\u89d2\u5ea6\u3002\u5982\u679c\u6cd5\u7ebf\u548c\u5149\u7684\u5939\u89d2\u6bd4\u8f83\u5c0f\uff0c\u6211\u4eec\u4f7f\u7528\u8f83\u4eae\u7684\u8272\u8c03\uff0c\u968f\u7740\u5939\u89d2\u53d8\u5927\uff0c\u9010\u6b65\u4f7f\u7528\u66f4\u6697\u7684\u8272\u8c03\u3002\u6362\u53e5\u8bdd\u8bf4\uff0c\u89d2\u5ea6\u4f59\u5f26\u503c\u5c06\u51b3\u5b9a\u8272\u8c03\u7684\u5f3a\u5ea6\u3002<\/p>\n\n\n\n<p>\u5173\u4e8e\u8ba1\u7b97\uff0c\u4e3b\u8981\u6839\u636ediffuse\u7684\u503c\u5f97\u533a\u95f4\u5bcc\u8d4b\u4e88\u4e0d\u540c\u989c\u8272\uff0c\u4f7f\u4e4b\u4ea7\u751f\u8fc7\u6e21\u7684\u989c\u8272\u6548\u679c\u3002\u5f53diffuse\u7684\u503c\u5927\u4e8e0.95\uff0c\u4f7f\u7528\u6700\u4eae\u7684\u989c\u8272\uff0c\u5f53diffuse\u7684\u503c\u5c0f\u4e8e0.25\uff0c\u4f7f\u7528\u6700\u6697\u7684\u989c\u8272\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">      vec3 color;\n      if (diff > 0.95)\n       color = vec3(1.0, 1.0, 1.0); \n      else if (diff > 0.5)\n       color = vec3(0.7, 0.7 ,0.7);\n      else if (diff > 0.25)\n       color = vec3(0.5, 0.5, 0.5);\n      else \n       color = vec3(0.3, 0.3, 0.3);\n  \u200b\n      vec3 diffuse = color * lightColor;<\/pre>\n\n\n\n<p>\u975e\u5361\u901a\u6548\u679c<\/p>\n\n\n\n<figure class=\"wp-block-image is-style-default\"><img decoding=\"async\" src=\"https:\/\/liyanliangpublic.oss-cn-hongkong.aliyuncs.com\/img\/TeaPot.png\" alt=\"\"\/><\/figure>\n\n\n\n<p><strong>CartoonShading.vs<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  #version 330 core\n  layout (location = 0) in vec3 aPos;\n  layout (location = 1) in vec3 aNormal;\n  \u200b\n  out vec3 FragPos;\n  out vec3 Normal;\n  \u200b\n  uniform mat4 model;\n  uniform mat4 view;\n  uniform mat4 projection;\n  \u200b\n  void main()\n  {\n      FragPos = vec3(model * vec4(aPos, 1.0));\n      Normal = mat3(transpose(inverse(model))) * aNormal;  \n  \u200b\n      gl_Position = projection * view * vec4(FragPos, 1.0);\n  }<\/pre>\n\n\n\n<p><strong>CartoonShading.fs<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  #version 330 core\n  out vec4 FragColor;\n  \u200b\n  in vec3 FragPos;\n  in vec3 Normal;\n  \u200b\n  uniform vec3 lightDir;\n  uniform vec3 lightColor;\n  uniform vec3 objectColor;\n  \u200b\n  void main()\n  {    \n      \/\/ diffuse\n      vec3 norm = normalize(Normal);\n      vec3 lightDir = normalize(-lightDir);\n      float diff = max(dot(norm, lightDir), 0.0);\n     \n      vec3 color;\n      if (diff > 0.95)\n       color = vec3(1.0, 1.0, 1.0); \n      else if (diff > 0.5)\n       color = vec3(0.7, 0.7 ,0.7);\n      else if (diff > 0.25)\n       color = vec3(0.5, 0.5, 0.5);\n      else \n       color = vec3(0.3, 0.3, 0.3);\n  \u200b\n      vec3 diffuse = color * lightColor;\n  \u200b\n      vec3 result =  diffuse * objectColor;\n  \u200b\n      FragColor = vec4(result, 1.0);\n  }<\/pre>\n\n\n\n<p><strong>main.cpp<\/strong><\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  #include &lt;glad\/glad.h>\n  #include &lt;GLFW\/glfw3.h>\n  \u200b\n  #include &lt;glm\/glm.hpp>\n  #include &lt;glm\/gtc\/matrix_transform.hpp>\n  #include &lt;glm\/gtc\/type_ptr.hpp>\n  \u200b\n  #include &lt;learnopengl\/shader_m.h>\n  #include &lt;learnopengl\/camera.h>\n  #include &lt;learnopengl\/model.h>\n  \u200b\n  #include &lt;iostream>\n  \u200b\n  void framebuffer_size_callback(GLFWwindow* window, int width, int height);\n  void mouse_callback(GLFWwindow* window, double xpos, double ypos);\n  void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);\n  void processInput(GLFWwindow *window);\n  \u200b\n  \/\/ settings\n  const unsigned int SCR_WIDTH = 800;\n  const unsigned int SCR_HEIGHT = 600;\n  \u200b\n  \/\/ camera\n  Camera camera(glm::vec3(0.0f, 4.0f, 15.0f));\n  float lastX = SCR_WIDTH \/ 2.0f;\n  float lastY = SCR_HEIGHT \/ 2.0f;\n  bool firstMouse = true;\n  \u200b\n  \/\/ timing\n  float deltaTime = 0.0f;\n  float lastFrame = 0.0f;\n  \u200b\n  int main()\n  {\n      \/\/ glfw: initialize and configure\n      \/\/ ------------------------------\n      glfwInit();\n      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);\n      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);\n      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);\n  \u200b\n  #ifdef __APPLE__\n      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);\n  #endif\n  \u200b\n      \/\/ glfw window creation\n      \/\/ --------------------\n      GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, \"CartoonShader\", NULL, NULL);\n      if (window == NULL)\n      {\n          std::cout &lt;&lt; \"Failed to create GLFW window\" &lt;&lt; std::endl;\n          glfwTerminate();\n          return -1;\n      }\n      glfwMakeContextCurrent(window);\n      glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);\n      glfwSetCursorPosCallback(window, mouse_callback);\n      glfwSetScrollCallback(window, scroll_callback);\n  \u200b\n      \/\/ tell GLFW to capture our mouse\n      \/\/glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);\n  \u200b\n      \/\/ glad: load all OpenGL function pointers\n      \/\/ ---------------------------------------\n      if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))\n      {\n          std::cout &lt;&lt; \"Failed to initialize GLAD\" &lt;&lt; std::endl;\n          return -1;\n      }\n  \u200b\n      \/\/ configure global opengl state\n      \/\/ -----------------------------\n      glEnable(GL_DEPTH_TEST);\n  \u200b\n      \/\/ build and compile shaders\n      \/\/ -------------------------\n      Shader ourShader(\"CartoonShading.vs\", \"CartoonShading.fs\");\n  \u200b\n      \/\/ load models\n      \/\/ -----------\n      Model ourModel(\"resources\/objects\/TeapotGlass\/teapot_n_glass.obj\");\n  \u200b\n  \u200b\n      \/\/ draw in wireframe\n      \/\/glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\n  \u200b\n      \/\/ render loop\n      \/\/ -----------\n      while (!glfwWindowShouldClose(window))\n      {\n          \/\/ per-frame time logic\n          \/\/ --------------------\n          float currentFrame = static_cast&lt;float>(glfwGetTime());\n          deltaTime = currentFrame - lastFrame;\n          lastFrame = currentFrame;\n  \u200b\n          \/\/ input\n          \/\/ -----\n          processInput(window);\n  \u200b\n          \/\/ render\n          \/\/ ------\n          glClearColor(0.05f, 0.05f, 0.05f, 1.0f);\n          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\n  \u200b\n          \/\/ don't forget to enable shader before setting uniforms\n          ourShader.use();\n          ourShader.setVec3(\"objectColor\", 1.0f, 0.5f, 0.31f);\n          ourShader.setVec3(\"lightColor\", 1.0f, 1.0f, 1.0f);\n          ourShader.setVec3(\"lightDir\", -0.2f, -10.0f, -10.0f);\n  \u200b\n          \/\/ view\/projection transformations\n          glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH \/ (float)SCR_HEIGHT, 0.1f, 100.0f);\n          glm::mat4 view = camera.GetViewMatrix();\n          ourShader.setMat4(\"projection\", projection);\n          ourShader.setMat4(\"view\", view);\n  \u200b\n          \/\/ render the loaded model\n          glm::mat4 model = glm::mat4(1.0f);\n          model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));\n          model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); \/\/ translate it down so it's at the center of the scene\n          model = glm::scale(model, glm::vec3(1.0f, 1.0f, 1.0f)); \/\/ it's a bit too big for our scene, so scale it down\n          ourShader.setMat4(\"model\", model);\n          ourModel.Draw(ourShader);\n  \u200b\n          \/\/ glfw: swap buffers and poll IO events (keys pressed\/released, mouse moved etc.)\n          \/\/ -------------------------------------------------------------------------------\n          glfwSwapBuffers(window);\n          glfwPollEvents();\n      }\n  \u200b\n      \/\/ glfw: terminate, clearing all previously allocated GLFW resources.\n      \/\/ ------------------------------------------------------------------\n      glfwTerminate();\n      return 0;\n  }\n  \u200b\n  \/\/ process all input: query GLFW whether relevant keys are pressed\/released this frame and react accordingly\n  \/\/ ---------------------------------------------------------------------------------------------------------\n  void processInput(GLFWwindow *window)\n  {\n      if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)\n          glfwSetWindowShouldClose(window, true);\n  \u200b\n      if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)\n          camera.ProcessKeyboard(FORWARD, deltaTime);\n      if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)\n          camera.ProcessKeyboard(BACKWARD, deltaTime);\n      if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)\n          camera.ProcessKeyboard(LEFT, deltaTime);\n      if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)\n          camera.ProcessKeyboard(RIGHT, deltaTime);\n  }\n  \u200b\n  \/\/ glfw: whenever the window size changed (by OS or user resize) this callback function executes\n  \/\/ ---------------------------------------------------------------------------------------------\n  void framebuffer_size_callback(GLFWwindow* window, int width, int height)\n  {\n      \/\/ make sure the viewport matches the new window dimensions; note that width and \n      \/\/ height will be significantly larger than specified on retina displays.\n      glViewport(0, 0, width, height);\n  }\n  \u200b\n  \/\/ glfw: whenever the mouse moves, this callback is called\n  \/\/ -------------------------------------------------------\n  void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)\n  {\n      float xpos = static_cast&lt;float>(xposIn);\n      float ypos = static_cast&lt;float>(yposIn);\n  \u200b\n      if (firstMouse)\n      {\n          lastX = xpos;\n          lastY = ypos;\n          firstMouse = false;\n      }\n  \u200b\n      float xoffset = xpos - lastX;\n      float yoffset = lastY - ypos; \/\/ reversed since y-coordinates go from bottom to top\n  \u200b\n      lastX = xpos;\n      lastY = ypos;\n  \u200b\n      camera.ProcessMouseMovement(xoffset, yoffset);\n  }\n  \u200b\n  \/\/ glfw: whenever the mouse scroll wheel scrolls, this callback is called\n  \/\/ ----------------------------------------------------------------------\n  void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)\n  {\n      camera.ProcessMouseScroll(static_cast&lt;float>(yoffset));\n  }\n  \u200b<\/pre>\n\n\n\n<p><strong>\u5b8c\u6574\u9879\u76ee\u4ee3\u7801<\/strong><\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/mc-liyanliang\/OpenGL-Shader\/tree\/master\">https:\/\/github.com\/mc-liyanliang\/OpenGL-Shader\/tree\/master<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u539f\u7406 \u8336\u58f6\u4e0a\u7684\u8272\u8c03\u901a\u8fc7\u89d2\u5ea6\u7684\u4f59\u5f26\u503c\u6765\u9009\u62e9\u7684\uff0c\u8fd9\u4e2a\u89d2\u5ea6\u662f\u6307\u5149\u7ebf\u548c\u9762\u7684\u6cd5\u7ebf\u4e4b\u95f4\u7684\u5939\u89d2\u89d2\u5ea6\u3002\u5982\u679c\u6cd5\u7ebf\u548c\u5149\u7684\u5939\u89d2\u6bd4\u8f83\u5c0f\uff0c\u6211\u4eec\u4f7f\u7528\u8f83\u4eae\u7684\u8272\u8c03\uff0c\u968f\u7740\u5939\u89d2\u53d8\u5927\uff0c\u9010\u6b65\u4f7f\u7528\u66f4\u6697\u7684\u8272\u8c03\u3002\u6362\u53e5\u8bdd\u8bf4\uff0c\u89d2\u5ea6\u4f59\u5f26\u503c\u5c06\u51b3\u5b9a\u8272\u8c03\u7684\u5f3a\u5ea6\u3002 \u5173\u4e8e\u8ba1\u7b97\uff0c\u4e3b\u8981\u6839\u636ediffuse\u7684\u503c\u5f97\u533a\u95f4\u5bcc\u8d4b\u4e88\u4e0d\u540c\u989c\u8272\uff0c\u4f7f\u4e4b\u4ea7\u751f\u8fc7\u6e21\u7684\u989c\u8272\u6548\u679c\u3002\u5f53diffuse\u7684\u503c\u5927\u4e8e0.95\uff0c\u4f7f\u7528\u6700\u4eae\u7684\u989c\u8272\uff0c\u5f53diffuse\u7684\u503c\u5c0f\u4e8e0.25\uff0c\u4f7f\u7528\u6700\u6697\u7684\u989c\u8272\u3002 \u975e\u5361\u901a\u6548\u679c CartoonShading.vs CartoonShading.fs main.cpp \u5b8c\u6574\u9879\u76ee\u4ee3\u7801 https:\/\/github.com\/mc-liyanliang\/OpenGL-Shader\/tree\/master<\/p>\n","protected":false},"author":1,"featured_media":466,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[],"class_list":["post-465","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-opengl"],"_links":{"self":[{"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/posts\/465","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/comments?post=465"}],"version-history":[{"count":1,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/posts\/465\/revisions"}],"predecessor-version":[{"id":467,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/posts\/465\/revisions\/467"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/media\/466"}],"wp:attachment":[{"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/media?parent=465"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/categories?post=465"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/liyanliang.net\/index.php\/wp-json\/wp\/v2\/tags?post=465"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}