Skip to content
  • 首页
  • 留言
  • 关于

Share the joys of programming and technology

OpenGL-卡通着色(Cartoon)

OpenGL-卡通着色(Cartoon)

2022年1月30日 liyanliang Comments 0 Comment
阅读次数: 327

原理

茶壶上的色调通过角度的余弦值来选择的,这个角度是指光线和面的法线之间的夹角角度。如果法线和光的夹角比较小,我们使用较亮的色调,随着夹角变大,逐步使用更暗的色调。换句话说,角度余弦值将决定色调的强度。

关于计算,主要根据diffuse的值得区间富赋予不同颜色,使之产生过渡的颜色效果。当diffuse的值大于0.95,使用最亮的颜色,当diffuse的值小于0.25,使用最暗的颜色。

      vec3 color;
      if (diff > 0.95)
       color = vec3(1.0, 1.0, 1.0); 
      else if (diff > 0.5)
       color = vec3(0.7, 0.7 ,0.7);
      else if (diff > 0.25)
       color = vec3(0.5, 0.5, 0.5);
      else 
       color = vec3(0.3, 0.3, 0.3);
  ​
      vec3 diffuse = color * lightColor;

非卡通效果

CartoonShading.vs

  #version 330 core
  layout (location = 0) in vec3 aPos;
  layout (location = 1) in vec3 aNormal;
  ​
  out vec3 FragPos;
  out vec3 Normal;
  ​
  uniform mat4 model;
  uniform mat4 view;
  uniform mat4 projection;
  ​
  void main()
  {
      FragPos = vec3(model * vec4(aPos, 1.0));
      Normal = mat3(transpose(inverse(model))) * aNormal;  
  ​
      gl_Position = projection * view * vec4(FragPos, 1.0);
  }

CartoonShading.fs

  #version 330 core
  out vec4 FragColor;
  ​
  in vec3 FragPos;
  in vec3 Normal;
  ​
  uniform vec3 lightDir;
  uniform vec3 lightColor;
  uniform vec3 objectColor;
  ​
  void main()
  {    
      // diffuse
      vec3 norm = normalize(Normal);
      vec3 lightDir = normalize(-lightDir);
      float diff = max(dot(norm, lightDir), 0.0);
     
      vec3 color;
      if (diff > 0.95)
       color = vec3(1.0, 1.0, 1.0); 
      else if (diff > 0.5)
       color = vec3(0.7, 0.7 ,0.7);
      else if (diff > 0.25)
       color = vec3(0.5, 0.5, 0.5);
      else 
       color = vec3(0.3, 0.3, 0.3);
  ​
      vec3 diffuse = color * lightColor;
  ​
      vec3 result =  diffuse * objectColor;
  ​
      FragColor = vec4(result, 1.0);
  }

main.cpp

  #include <glad/glad.h>
  #include <GLFW/glfw3.h>
  ​
  #include <glm/glm.hpp>
  #include <glm/gtc/matrix_transform.hpp>
  #include <glm/gtc/type_ptr.hpp>
  ​
  #include <learnopengl/shader_m.h>
  #include <learnopengl/camera.h>
  #include <learnopengl/model.h>
  ​
  #include <iostream>
  ​
  void framebuffer_size_callback(GLFWwindow* window, int width, int height);
  void mouse_callback(GLFWwindow* window, double xpos, double ypos);
  void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
  void processInput(GLFWwindow *window);
  ​
  // settings
  const unsigned int SCR_WIDTH = 800;
  const unsigned int SCR_HEIGHT = 600;
  ​
  // camera
  Camera camera(glm::vec3(0.0f, 4.0f, 15.0f));
  float lastX = SCR_WIDTH / 2.0f;
  float lastY = SCR_HEIGHT / 2.0f;
  bool firstMouse = true;
  ​
  // timing
  float deltaTime = 0.0f;
  float lastFrame = 0.0f;
  ​
  int main()
  {
      // glfw: initialize and configure
      // ------------------------------
      glfwInit();
      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  ​
  #ifdef __APPLE__
      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
  #endif
  ​
      // glfw window creation
      // --------------------
      GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "CartoonShader", NULL, NULL);
      if (window == NULL)
      {
          std::cout << "Failed to create GLFW window" << std::endl;
          glfwTerminate();
          return -1;
      }
      glfwMakeContextCurrent(window);
      glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
      glfwSetCursorPosCallback(window, mouse_callback);
      glfwSetScrollCallback(window, scroll_callback);
  ​
      // tell GLFW to capture our mouse
      //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  ​
      // glad: load all OpenGL function pointers
      // ---------------------------------------
      if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
      {
          std::cout << "Failed to initialize GLAD" << std::endl;
          return -1;
      }
  ​
      // configure global opengl state
      // -----------------------------
      glEnable(GL_DEPTH_TEST);
  ​
      // build and compile shaders
      // -------------------------
      Shader ourShader("CartoonShading.vs", "CartoonShading.fs");
  ​
      // load models
      // -----------
      Model ourModel("resources/objects/TeapotGlass/teapot_n_glass.obj");
  ​
  ​
      // draw in wireframe
      //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  ​
      // render loop
      // -----------
      while (!glfwWindowShouldClose(window))
      {
          // per-frame time logic
          // --------------------
          float currentFrame = static_cast<float>(glfwGetTime());
          deltaTime = currentFrame - lastFrame;
          lastFrame = currentFrame;
  ​
          // input
          // -----
          processInput(window);
  ​
          // render
          // ------
          glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  ​
          // don't forget to enable shader before setting uniforms
          ourShader.use();
          ourShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
          ourShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
          ourShader.setVec3("lightDir", -0.2f, -10.0f, -10.0f);
  ​
          // view/projection transformations
          glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
          glm::mat4 view = camera.GetViewMatrix();
          ourShader.setMat4("projection", projection);
          ourShader.setMat4("view", view);
  ​
          // render the loaded model
          glm::mat4 model = glm::mat4(1.0f);
          model = glm::rotate(model, (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));
          model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); // translate it down so it's at the center of the scene
          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
          ourShader.setMat4("model", model);
          ourModel.Draw(ourShader);
  ​
          // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
          // -------------------------------------------------------------------------------
          glfwSwapBuffers(window);
          glfwPollEvents();
      }
  ​
      // glfw: terminate, clearing all previously allocated GLFW resources.
      // ------------------------------------------------------------------
      glfwTerminate();
      return 0;
  }
  ​
  // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
  // ---------------------------------------------------------------------------------------------------------
  void processInput(GLFWwindow *window)
  {
      if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
          glfwSetWindowShouldClose(window, true);
  ​
      if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
          camera.ProcessKeyboard(FORWARD, deltaTime);
      if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
          camera.ProcessKeyboard(BACKWARD, deltaTime);
      if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
          camera.ProcessKeyboard(LEFT, deltaTime);
      if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
          camera.ProcessKeyboard(RIGHT, deltaTime);
  }
  ​
  // glfw: whenever the window size changed (by OS or user resize) this callback function executes
  // ---------------------------------------------------------------------------------------------
  void framebuffer_size_callback(GLFWwindow* window, int width, int height)
  {
      // make sure the viewport matches the new window dimensions; note that width and 
      // height will be significantly larger than specified on retina displays.
      glViewport(0, 0, width, height);
  }
  ​
  // glfw: whenever the mouse moves, this callback is called
  // -------------------------------------------------------
  void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
  {
      float xpos = static_cast<float>(xposIn);
      float ypos = static_cast<float>(yposIn);
  ​
      if (firstMouse)
      {
          lastX = xpos;
          lastY = ypos;
          firstMouse = false;
      }
  ​
      float xoffset = xpos - lastX;
      float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
  ​
      lastX = xpos;
      lastY = ypos;
  ​
      camera.ProcessMouseMovement(xoffset, yoffset);
  }
  ​
  // glfw: whenever the mouse scroll wheel scrolls, this callback is called
  // ----------------------------------------------------------------------
  void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
  {
      camera.ProcessMouseScroll(static_cast<float>(yoffset));
  }
  ​

完整项目代码

https://github.com/mc-liyanliang/OpenGL-Shader/tree/master

相关文章

  • 提取最小封闭区域提取最小封闭区域
  • 创建模态对话框绘制直线-ObjectARX开发视频创建模态对话框绘制直线-ObjectARX开发视频
  • Midas XD [错误] 右侧挡土墙的最下端深度必须小于地基的最下端深度Midas XD [错误] 右侧挡土墙的最下端深度必须小于地基的最下端深度
  • Civil Designer开发-检测规范自动生成控制截面Civil Designer开发-检测规范自动生成控制截面
  • LearnOpenGL脑图汇总LearnOpenGL脑图汇总
  • midas XD2020的开发

OpenGL

Post navigation

PREVIOUS
OpenGL几何着色器实现贝塞尔曲线
NEXT
OpenGL-法线贴图(Normal Mapping)

发表回复 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注

近期文章

  • ANR崩溃日志查看方法
  • 通过数学方法来计算short类型的变量w的低八位x和高八位
  • 3dTiles数据解析
  • Games101和Games202脑图汇总
  • LearnOpenGL脑图汇总
  • IBL计算总结
  • C++实现一个简单的语言解释器
  • OpenGL-法线贴图(Normal Mapping)
  • OpenGL-卡通着色(Cartoon)
  • OpenGL几何着色器实现贝塞尔曲线
  • WinDbg检查内存泄漏
  • OpenGL雾化效果实现-每像素雾化
  • OpenGL实现billboard效果(CPU)
  • 算法:寻找异常数字
  • OpenGL 几何着色器的应用
  • Midas XD-构件详图开发
  • Midas XD-选筋助手开发
  • Civil Designer开发-检测规范自动生成控制截面
  • Civil Designer开发-公路桥梁承载能力检算评定
  • Midas W-满堂支架快速建模助手开发

全站热点

  • C++编写的情人节小程序 (2,083)
  • 提取最小封闭区域 (1,696)
  • Modern OpenGL绘制圆柱体 (1,607)
  • OpenGL开发环境搭建-GLFW与GLAD配置 超详细 (1,465)
  • 截面特性计算程序-附源码 (1,290)
  • OpenGL绘制旋转立方体 (1,110)
  • 判断一个点是否在闭合区域内 (1,032)
  • WordPress分页插件 – WP-PageNavi的使用(替换现有脚本) (948)
  • OpenGL实现billboard效果(CPU) (865)
  • Midas W-满堂支架快速建模助手开发 (837)
  • 从DLL中动态加载一个函数:LoadLibrary和GetProcAddress的使用 (748)
  • Midas XD [错误] 右侧挡土墙的最下端深度必须小于地基的最下端深度 (709)
  • 两跨连续梁影响线绘制-附源码 (686)
  • 土木想往土木软件开发方向发展,应该如何准备 (680)
  • OpenGL几何着色器实现贝塞尔曲线 (664)
  • 通过Spy++抓取窗口以查询对话框id (613)
  • 使用ODA数据库出现 “ODA_ASSUME”: 找不到标识符的错误 (547)
  • #pragma message 编译时提示信息 (527)
  • OpenGL雾化效果实现-每像素雾化 (508)
  • midas XD2020的开发 (476)

分类

  • C# (3)
  • C++ (19)
  • GIS (1)
  • MFC (3)
  • ObjectARX (2)
  • OpenGL (11)
  • Revit开发 (1)
  • 学习笔记 (2)
  • 岩土 (2)
  • 算法 (1)
  • 结构设计 (7)
  • 职场生涯 (1)
  • 计算几何 (3)

归档

  • 2024 年 12 月 (1)
  • 2024 年 10 月 (1)
  • 2024 年 9 月 (1)
  • 2023 年 3 月 (2)
  • 2022 年 10 月 (1)
  • 2022 年 3 月 (1)
  • 2022 年 2 月 (1)
  • 2022 年 1 月 (5)
  • 2021 年 11 月 (7)
  • 2021 年 6 月 (3)
  • 2021 年 5 月 (2)
  • 2021 年 3 月 (2)
  • 2021 年 2 月 (8)
  • 2021 年 1 月 (18)

标签

3dtiles anr Bezier Curves BillBoard C++ CDN CivilDesigner DLL EasyX fog glTF MFC Midas W Midas XD NormalMapping ObjectARX ODA OpenGL OpenXML Open XML PBR revit WinDbg 基坑设计 影响线 截面特性 桥梁 桥梁检测 桥梁设计 算法 计算几何 设计模式

书签

  • 李燕良的CSDN
  • 崔济东的博客
  • C++爱好者博客
  • 陈学伟的博客
  • 贾苏的博客
  • 陈睦锋的博客
  • 孙勇的博客

统计

  • 0
  • 189
  • 97
  • 388
  • 145
  • 268,892
  • 77,784

实时访问地域

© 2025   liyanliang.net Copyright. All Rights Reserved.