LearnOpenGL 及 ShaderToy 的 CMake 构建框架

文章目录

    • 构建目标
    • 具体框架
      • 根目录
      • src 目录
      • app 目录
      • import.cmake
      • 其他 CMake 函数
    • 使用框架
    • 实际效果
      • 摄像机坐标变换
      • 使用 assimp 库加载模型
      • shadertoy 测试 framebuffer 离屏渲染
    • 其他

为了复习 OpenGL(主要是看到 shadertoy 上有好玩的着色器),所以打算重新写一下构建框架来适配 shadertoy 到本地 GLSL 代码的转换,这样 shadertoy 上有什么好玩的着色器都可以直接复制下来在本地运行(当然片段着色器里面有一些坐标还是需要转换一下的)。

构建目标

  • 构建源码目录前,扫描导入库目录下的所有动态库和静态库并引入为当前 CMake 的 target,这样其他二进制可执行文件目标在构建的时候可以随时链接全局 target 名称
  • 适配 Linux 和 Windows 系统平台的动态库和静态库链接编译,Windows 平台基于 MSVC 编译工具链的情况比较复杂一些,有一些坑
  • 允许灵活的添加测试目录或者指定要构建的单元测试源代码
  • 混合编译 C/C++
  • 指定每一个 shader 主程序编译所需要链接的动态库或者静态库

具体框架

主目录结构如下:

F:\FREDOM\WORKSPACE\PLAY-CC\SHADERTOY
├─.vscode
├─app
│  ├─learnopengl
│  └─shadertoy
├─assets
│  ├─audio
│  ├─model
│  ├─shader
│  │  ├─learnopengl
│  │  │  ├─frag
│  │  │  ├─geom
│  │  │  └─vert
│  │  └─shadertoy
│  └─texture
├─build
├─cmake
├─include
│  ├─assimp
│  ├─dummy
│  ├─glad
│  ├─GLFW
│  ├─glm
│  ├─KHR
│  ├─learnopengl
│  ├─stb_image
│  └─utils
├─lib
│  ├─assimp
│  ├─glfw3
│  └─opengl32
├─src
│  ├─dummy
│  ├─glad
│  ├─stb_image
│  └─utils
└─test├─log└─misc
  • app:存放每个 main 函数入口源文件,一个文件就是一个应用程序的入口点,可以编写多个入口 main ,这当然是为了方便测试不同的 shadertoy 代码了,如果只能编译一个 main 的话太不方便了(所以用 VScode 比 VisualStudio 灵活)
  • assets:资源文件目录,比如纹理贴图、模型材质、着色器、音频
  • build:构建目录,存放构建的动态库静态库以及构建完之后安装(install 的概念大概就是复制构建产物到一个单独的目录下方便取用)
  • cmake:存放一些 cmake 脚本,比如自动搜索头文件目录或者扫描库目录下的库文件
  • include:存放所有头文件
  • lib:外部引入的第三方库(静态或动态),比如 glfw3、assimp 等等
  • src:头文件对应的实现文件,这一般是项目内部的构件库的实现文件目录,因为外部导入的头文件一般使用的当然是对应的构建好的库文件,实现都已经在库文件中了而不是实际的源代码,不然每次编译几百个源代码那真是有的好受的
  • test:存放各种单元测试文件

根目录

首先是项目根目录的 CMakeLists.txt ,既然是根目录的配置文件,当然是负责一些全局的设置和引入其他子目录了,不可能根目录下的一个配置文件完成所有构建目标的配置,每个目录下的源文件构建由子目录对应的构建配置 CMakeLists.txt 决定如何完成,根目录应该设置好当前编译系统名称以及全局的宏开关。

具体内容如下:

CMAKE_MINIMUM_REQUIRED(VERSION 3.21)
PROJECT(temp)MESSAGE("build system: ${CMAKE_SYSTEM_NAME}")
MESSAGE("${CMAKE_CURRENT_BINARY_DIR}")
SET(CMAKE_CXX_STANDARD 17)
SET(CAMKE_C_STANDARD 17)SET(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/install)
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) # binary executable
# set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) # shared library
# set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) # static library# help vscode find compiler_command.json
ADD_DEFINITIONS(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON)# other define in source code
ADD_COMPILE_DEFINITIONS(CXX_COMPILER_ID_${CMAKE_CXX_COMPILER_ID})
ADD_COMPILE_DEFINITIONS(C_COMPILER_ID_${CMAKE_C_COMPILER_ID})
STRING(TOUPPER ${CMAKE_BUILD_TYPE} build_type_str)
ADD_COMPILE_DEFINITIONS(${build_type_str})IF(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")MESSAGE("compiler tool chain MSVC, all symbols will export by default")SET(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
ENDIF()INCLUDE(cmake/macro.cmake)INCLUDE(import.cmake)ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(app)
ADD_SUBDIRECTORY(test)

其中这一段代码:

IF(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")MESSAGE("compiler tool chain MSVC, all symbols will export by default")SET(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
ENDIF()

是为了在 Windows 下使用 MSVC 编译时,默认导出所有函数符号,C++ 背景下接口的声明和实现就是函数的头文件和动态库或静态库,Windows 下默认函数符号是不导出的,必须手动使用 __declspec(dllexport) 来标注某个函数的导出,这是对于库的构建者来说的,同时对于使用该库的用户,如果使用了分发的接口头文件中的某个接口(函数),也即头文件中给出的接口函数都是开发者编译的动态库中允许导出的符号,则使用者使用函数之前也需要添加属性 __declspec(dllimport) (不知道这么理解对不对);如果 MSVC 编译静态库的话就没有这么麻烦,编译完输出文件之后在链接阶段直接把静态库链接到二进制可执行文件,没有什么导出不导出的问题了。

但是不幸的是因为我自己还有一个子目录是写了一个 C/C++ 的打印日志功能的,其中 C 的打印日志使用了全局的缓冲数组,因此在头文件中进行了 extern 的全局变量声明;注意这是一个常见的错误:不要在头文件中直接声明全局变量,否则容易发生重定义错误,这不是 #ifndef#define 能解决的重复包含问题(防止一个编译单元内多次包含,但是现在的情况是多个编译单元),而是在链接阶段如果存在多个使用了该头文件对应的链接库的链接输出文件,后续再次参与链接,那么多个这样的文件中的导出符号表将使得链接器无法选择重名的符号。

src 目录

注意,先包含 src 目录再包含 app 目录,因为 src 下可能有构建的目标被 app 中的主应用程序依赖,比如我这里的 log 的一些功能就是 test 中所依赖的实现,那么必须先编译 src 下面的目标,这样库目标存在之后才能被其他子目录的编译构建链接使用。

SET(lib_type SHARED)
# SET(lib_type STATIC)# build dummy
FILE(GLOB module_dummy_src_listdummy/*.cppdummy/*.ccdummy/*.c
)
PRINT_LIST("${module_dummy_src_list}" "MODULE [dummy] SRC" "")
ADD_LIBRARY(dummy ${lib_type} ${module_dummy_src_list})
INSTALL(TARGETS dummy RUNTIME DESTINATION binARCHIVE DESTINATION lib
)# build utils
FILE(GLOB module_utils_src_listutils/*.cpputils/*.ccutils/*.c
)
PRINT_LIST("${module_utils_src_list}" "MODULE [utils] SRC" "")
ADD_LIBRARY(utils ${lib_type} ${module_utils_src_list})
IF(lib_type STREQUAL "SHARED")# this is for MSVC usage to export global data symbols# CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS only exports methodIF(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")TARGET_COMPILE_DEFINITIONS(utilsPRIVATE DLLCOMPILE # can not use PUBLIC attr? That's weird...)ENDIF()
ENDIF()
INSTALL(TARGETS utils RUNTIME DESTINATION binARCHIVE DESTINATION lib
)# build other
FILE(GLOB module_other_src_list./*.cpp./*.cc./*.c
)
PRINT_LIST("${module_other_src_list}" "MODULE [other] SRC" "")
ADD_LIBRARY(other ${lib_type} ${module_other_src_list})
INSTALL(TARGETS other RUNTIME DESTINATION binARCHIVE DESTINATION lib
)# build glad
FILE(GLOB module_glad_src_listglad/*.cppglad/*.ccglad/*.c
)
PRINT_LIST("${module_glad_src_list}" "MODULE [glad] SRC" "")
ADD_LIBRARY(glad ${lib_type} ${module_glad_src_list})
IF(lib_type STREQUAL "SHARED")TARGET_COMPILE_DEFINITIONS(gladPUBLIC GLAD_GLAPI_EXPORTPRIVATE GLAD_GLAPI_EXPORT_BUILD)
ENDIF()
INSTALL(TARGETS gladRUNTIME DESTINATION binARCHIVE DESTINATION lib
)# build stb_image
FILE(GLOB module_stb_image_src_liststb_image/*.cppstb_image/*.ccstb_image/*.c
)
PRINT_LIST("${module_stb_image_src_list}" "MODULE [stb_image] SRC" "")
ADD_LIBRARY(stb_image ${lib_type} ${module_stb_image_src_list})
INSTALL(TARGETS stb_imageRUNTIME DESTINATION binARCHIVE DESTINATION lib
)

这里选择的是构建动态库,其实 CMake 自己默认有一个这个宏的 BUILD_SHARED_LIBS 其实用哪个都行,这里我是自定义了 lib_type 变量,方便构建的时候直接传入这个变量代表构建库类型。

INSTALL 这些构建好的目标是必须的,二进制可执行文件如果使用的是动态库链接,那么运行时会在当前的目录下寻找链接库或者去系统环境变量指定的目录下寻找,为了在 VScode 中能够 F5 一键 Debug ,构建的动态库要放到编译产物的输出安装目录了,静态库的话安不安装倒无所谓……

这里当然可以写一个 FOREACH 循环扫描当前目录的每个子目录然后执行构建,但是这样有时候想单独指定某个库是动态的还是静态的就不方便了,这里我还是拆开每个子目录单独指定构建流程。

后续如果有其他的自定义实现函数模块,可以在 src 目录下新建一个子目录存放模块相关的实现文件,然后在 src 目录下的 CMakeLists.txt 文件里面添加对应的模块构建流程,就是把其他模块构建流程复制一遍改一下路径。

app 目录

app 目录就是指定要构建哪些主程序的入口文件,然后链接所需的库,再把链接过的动态库放到和输出二进制文件同一个目录方便调试。

# user should be responsible for installing the dynamic
# library that they used to link with any exe bin# ============================= main =============================
SET(lib_inn_static)
SET(lib_inn_shared)
SET(lib_ext_static)
SET(lib_ext_shared)
SET(lib_all${lib_inn_static}${lib_inn_shared}${lib_ext_static}${lib_ext_shared}
)
ADD_EXECUTABLE(main main.cpp)
IF(lib_all)TARGET_LINK_LIBRARIES(main ${lib_all})# move dll/so to bin dir# for inner lib and external staticINSTALL(TARGETS ${lib_inn}RUNTIME DESTINATION binARCHIVE DESTINATION lib)
ENDIF()
INSTALL(TARGETS main RUNTIME DESTINATION bin)# ============================= learnopengl =============================
SET(learnopengl_src_listlearnopengl/triangle.cpplearnopengl/texture.cpplearnopengl/transform.cpplearnopengl/coordinate.cpplearnopengl/camera.cpplearnopengl/framebuffer.cpplearnopengl/model_load.cpp
)SET(lib_inn_static)
SET(lib_inn_shared glad stb_image)
SET(lib_ext_static opengl32)
SET(lib_ext_shared glfw3 assimp-vc143-mtd)
SET(lib_all${lib_inn_static}${lib_inn_shared}${lib_ext_static}${lib_ext_shared}
)FOREACH(_src_file ${learnopengl_src_list})GET_FILENAME_COMPONENT(_src_name ${_src_file} NAME_WE)ADD_EXECUTABLE(${_src_name} ${_src_file})IF(lib_all)TARGET_LINK_LIBRARIES(${_src_name} ${lib_all})ENDIF()INSTALL(TARGETS ${_src_name} RUNTIME DESTINATION bin)
ENDFOREACH()IF(lib_all)# move dll/so to bin dir# for internal targetsINSTALL(TARGETS ${lib_inn_shared} ${lib_inn_static}RUNTIME DESTINATION binARCHIVE DESTINATION lib)# for external targetsINSTALL(IMPORTED_RUNTIME_ARTIFACTS ${lib_ext_shared})
ENDIF()# ============================= shadertoy =============================
SET(shadertoy_src_listshadertoy/isovalues.cppshadertoy/simple.cpp
)SET(lib_inn_static)
SET(lib_inn_shared glad stb_image)
SET(lib_ext_static opengl32)
SET(lib_ext_shared glfw3 assimp-vc143-mtd)
SET(lib_all${lib_inn_static}${lib_inn_shared}${lib_ext_static}${lib_ext_shared}
)FOREACH(_src_file ${shadertoy_src_list})GET_FILENAME_COMPONENT(_src_name ${_src_file} NAME_WE)ADD_EXECUTABLE(${_src_name} ${_src_file})IF(lib_all)TARGET_LINK_LIBRARIES(${_src_name} ${lib_all})ENDIF()INSTALL(TARGETS ${_src_name} RUNTIME DESTINATION bin)
ENDFOREACH()IF(lib_all)# move dll/so to bin dir# for internal targetsINSTALL(TARGETS ${lib_inn_shared} ${lib_inn_static}RUNTIME DESTINATION binARCHIVE DESTINATION lib)# for external targetsINSTALL(IMPORTED_RUNTIME_ARTIFACTS ${lib_ext_shared})
ENDIF()

同样也是可以收集所有子目录然后统一执行构建流程的,但问题是不是每个源文件都依赖于相同的库,虽然说直接把所有库放到一个列表里面让链接器自己去按需取用也没问题,但是感觉不够优雅而且不够精准,我就是想指定每个每个主程序所依赖的库……

比如这里分了 内部静态、内部动态、外部静态、外部动态 四个类型的库列表,按照源文件所依赖的函数填写对应的库名称即可。

SET(lib_inn_static)
SET(lib_inn_shared glad stb_image)
SET(lib_ext_static opengl32)
SET(lib_ext_shared glfw3 assimp-vc143-mtd)
SET(lib_all${lib_inn_static}${lib_inn_shared}${lib_ext_static}${lib_ext_shared}
)

import.cmake

这个文件主要是为了扫描所有的头文件目录和外部库,然后告诉 CMake 。如果不告诉 CMake 头文件的目录路径而是使用默认的 VScode C++ Intellisense ,那么就只能使用双引号+头文件的文件名来引入头文件,可是很多时候头文件都是带相对路径的,所以必须告诉 VScode 的 C++ Intellisense 插件这些信息才不会弹出头文件包含错误提示(虽然编译的时候没事,但是看着总是不顺眼的嘛)。

# 包含头文件目录
FIND_HDRS(include return_hdr_dir_list)
PRINT_LIST("${return_hdr_dir_list}" "HEADER" "")
INCLUDE_DIRECTORIES(${return_hdr_dir_list})SET(lib_import_shared_list)
SET(lib_import_static_list)SET(lib_shared_suffix "dll") # linux: so
SET(lib_static_suffix "lib") # linux: aLINK_DIRECTORIES(lib/glfw3)
FIND_LIBS(lib/glfw3 ${lib_shared_suffix} return_lib_name_list return_lib_path_list)
MAKE_LIBS_TARGET("${return_lib_name_list}" "${return_lib_path_list}" SHARED)
LIST(APPEND lib_import_shared_list ${return_lib_name_list})LINK_DIRECTORIES(lib/assimp)
FIND_LIBS(lib/assimp ${lib_shared_suffix} return_lib_name_list return_lib_path_list)
MAKE_LIBS_TARGET("${return_lib_name_list}" "${return_lib_path_list}" SHARED)
LIST(APPEND lib_import_shared_list ${return_lib_name_list})LINK_DIRECTORIES(lib/opengl32)
FIND_LIBS(lib/opengl32 ${lib_static_suffix} return_lib_name_list return_lib_path_list)
MAKE_LIBS_TARGET("${return_lib_name_list}" "${return_lib_path_list}" STATIC)
LIST(APPEND lib_import_static_list ${return_lib_name_list})PRINT_LIST("${lib_import_shared_list}" "IMPORT LIB SHARED" "")
PRINT_LIST("${lib_import_static_list}" "IMPORT LIB STATIC" "")

同理每次导入了新的库和头文件,应该在这里添加新的扫描(头文件目录是递归扫描的,但是库目录我没有这样做)。

其他 CMake 函数

还有一些上面用到的 CMake 函数宏在 cmake 目录下,主要是搜索头文件,库文件以及打印列表的函数。

FUNCTION(FIND_LIBS lib_dir suffix return_lib_name_list return_lib_path_list)UNSET(return_lib_name_list CACHE)UNSET(return_lib_path_list CACHE)FILE(GLOB lib_path_list${lib_dir}/*.${suffix})FOREACH(_lib_path ${lib_path_list})GET_FILENAME_COMPONENT(_lib_name ${_lib_path} NAME_WE)# MESSAGE("_lib_name ${_lib_name}")LIST(APPEND lib_name_list ${_lib_name})ENDFOREACH(_lib_path ${lib_path_list})# MESSAGE("lib_name_list ${lib_name_list}")# MESSAGE("lib_path_list ${lib_path_list}")SET(return_lib_name_list ${lib_name_list} PARENT_SCOPE)SET(return_lib_path_list ${lib_path_list} PARENT_SCOPE)
ENDFUNCTION()FUNCTION(MAKE_LIBS_TARGET lib_name_list lib_path_list lib_type)IF(NOT lib_type STREQUAL "SHARED" AND NOT lib_type STREQUAL "STATIC")MESSAGE(FATAL_ERROR "unknown lib type ${lib_type}")ENDIF()LIST(LENGTH lib_name_list num_lib_name)LIST(LENGTH lib_path_list num_lib_path)IF(NOT num_lib_name EQUAL num_lib_path)MESSAGE(FATAL_ERROR "number of name and path of libs not equal")ENDIF()FOREACH(i RANGE 0 ${num_lib_name})IF(${i} EQUAL ${num_lib_name})BREAK()ENDIF()LIST(GET lib_name_list ${i} _lib_name)LIST(GET lib_path_list ${i} _lib_path)ADD_LIBRARY(${_lib_name} ${lib_type} IMPORTED GLOBAL)SET_PROPERTY(TARGET ${_lib_name}PROPERTY IMPORTED_LOCATION ${_lib_path})# only for windows dll, that's suckIF(CMAKE_SYSTEM_NAME STREQUAL "Windows" AND lib_type STREQUAL "SHARED")GET_FILENAME_COMPONENT(_lib_dir ${_lib_path} DIRECTORY)SET_PROPERTY(TARGET ${_lib_name}PROPERTY IMPORTED_IMPLIB ${_lib_dir}/${_lib_name}.lib # may be this name rule?)ENDIF()ENDFOREACH()ENDFUNCTION()FUNCTION(FIND_HDRS hdr_dir return_hdr_dir_list)UNSET(hdr_dir_list CACHE)SET(hdr_dir_list)FILE(GLOB_RECURSE hdr_path_list${hdr_dir}/*.h${hdr_dir}/*.hpp)SET(hdr_dir_list ${hdr_dir})FOREACH(_hdr_path ${hdr_path_list})GET_FILENAME_COMPONENT(_hdr_dir ${_hdr_path} PATH)LIST(APPEND hdr_dir_list ${_hdr_dir})ENDFOREACH(_hdr_path ${hdr_path_list})LIST(REMOVE_DUPLICATES hdr_dir_list)SET(return_hdr_dir_list ${hdr_dir_list} PARENT_SCOPE)
ENDFUNCTION()# print list item
FUNCTION(PRINT_LIST list_item title prefix)IF(NOT list_item OR (list_item STREQUAL ""))RETURN()ENDIF()MESSAGE("┌────────────────── ${title}")FOREACH(item ${list_item})MESSAGE("│ ${prefix} ${item}")ENDFOREACH()MESSAGE("└──────────────────]\n")
ENDFUNCTION()

使用框架

因为是在 Windows 平台下编译的,所以能用 MSVC 当然还是用 MSVC,虽然感觉有些难用就是了。VScode 的 CMake 插件可以选择构建的工具链,比如 MinGW 或者 MSVC ,MinGW 的 g++/gcc 构建比较简单,这里我主要是测试 MSVC 构建流程。所以如果使用 MSVC 的话,那么是无法直接通过 VScode 打开项目目录构建的:

  • 此时 C++ Intellisense 没有 MSVC 的扫描权限,无法找到标准库头文件诸如 iostream 等
  • 此时编译可以完成,但是编译出来的动态库不具有链接性(可能是我个人的原因?)

正确的操作方式应该是打开 VisualStudio 开发者命令行工具,然后 cd 到项目的目录下,再使用 code . 命令在此目录下打开 VScode ,这样才能检索到 MSVC 和 WindowsSDK 的头文件目录以及标准库。

CMake 配置时的输出如下:

[main] 正在配置项目: shadertoy 
[proc] 执行命令: E:\CMake\bin\cmake.EXE -DCMAKE_EXPORT_COMPILE_COMMANDS=ON --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -SF:/fredom/workspace/play-cc/shadertoy -Bf:/fredom/workspace/play-cc/shadertoy/build -G "Visual Studio 17 2022" -T host=x64 -A x64
[cmake] Not searching for unused variables given on the command line.
[cmake] -- Selecting Windows SDK version 10.0.22621.0 to target Windows 10.0.19045.
[cmake] -- The C compiler identification is MSVC 19.38.33134.0
[cmake] -- The CXX compiler identification is MSVC 19.38.33134.0
[cmake] -- Detecting C compiler ABI info
[cmake] -- Detecting C compiler ABI info - done
[cmake] -- Check for working C compiler: E:/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/cl.exe - skipped
[cmake] -- Detecting C compile features
[cmake] -- Detecting C compile features - done
[cmake] -- Detecting CXX compiler ABI info
[cmake] -- Detecting CXX compiler ABI info - done
[cmake] -- Check for working CXX compiler: E:/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/cl.exe - skipped
[cmake] -- Detecting CXX compile features
[cmake] -- Detecting CXX compile features - done
[cmake] build system: Windows
[cmake] F:/fredom/workspace/play-cc/shadertoy/build
[cmake] compiler tool chain MSVC, all symbols will export by default
[cmake] ┌────────────────── HEADER
[cmake] │  include
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/GLFW
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/KHR
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/assimp
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/assimp/Compiler
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/dummy
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/dummy/dummy2
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glad
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glm
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glm/detail
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glm/ext
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glm/gtc
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glm/gtx
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/glm/simd
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/learnopengl
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/stb_image
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/include/utils
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── IMPORT LIB SHARED
[cmake] │  glfw3
[cmake] │  assimp-vc143-mtd
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── IMPORT LIB STATIC
[cmake] │  opengl32
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── MODULE [dummy] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/dummy/dummy.cpp
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/dummy/dummy2.cpp
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── MODULE [utils] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/utils/clog.c
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/utils/cpplog.cpp
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── MODULE [other] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/./add.cpp
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/./mathnipet.cpp
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── MODULE [glad] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/glad/glad.c
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── MODULE [stb_image] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/src/stb_image/stb_image.cpp
[cmake] └──────────────────]
[cmake] 
[cmake] ┌────────────────── TEST [LOG] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/test/log/clog_test.c
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/test/log/cpplog_test.cpp
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/test/log/mixlog_test.cpp
[cmake] └──────────────────]
[cmake] 
[cmake] -- Configuring done (4.9s)
[cmake] ┌────────────────── TEST [misc] SRC
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/test/misc/enummap.cpp
[cmake] │  F:/fredom/workspace/play-cc/shadertoy/test/misc/include.cpp
[cmake] └──────────────────]
[cmake] 
[cmake] -- Generating done (0.1s)
[cmake] -- Build files have been written to: F:/fredom/workspace/play-cc/shadertoy/build
[visual-studio] 为 E:\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat 修补从 C:\Program Files (x86)\Windows Kits\10\bin\x64 到 C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64 的 Windows SDK 路径

编译完所有项目之后,可以直接在 install 目录里面的 bin 目录双击执行可调式文件,或者直接在 VScode 里面 F5 一键调试。目前验证的 learnopengl 教程只有几个,没有全部验证,不过也就是简单的把 LearnOpenGL 的代码拷贝到本地做一下资源路径的修改罢了,因为本意还是给 ShaderToy 准备的,LearnOpenGL 是用来验证 MSVC 生成的其他动态库可以链接使用,功能正常。

实际效果

摄像机坐标变换

典型的 FPS 移动摄像机

使用 assimp 库加载模型

背包模型导入 obj 以及材质文件和纹理贴图之后的效果

shadertoy 测试 framebuffer 离屏渲染

帧缓冲重新作为片段着色器输出的测试

其他

相关的代码和 MSVC 2022 编译的 GLAD、assimp 库之类的放在代码仓库了,有点大,200 多 MB,好像 GitHub 最多支持 5 GB 的仓库大小,其实主要是 assets 资源文件里面纹理贴图有点多,构建出来的动态库本身倒不是很大。下载代码下来之后记得把 build 目录删除清空,重新执行 CMake 配置缓存。注意代码仓库里面包含的 lib 下的动态库都是 MSVC 2022 编译构建的,如果使用 MinGW 的话,需要自己把对应的 GLAD 还有 assimp 库用 MinGW 生成动态库,复制到项目的 lib 目录下对应的子目录。Windows 下编译动态库虽然也会输出 lib 文件,但是这个文件是用于动态库链接的时候找导出符号的,和平常的 lib 静态库文件不是一个东西……所以如果在 Windows 下使用 MSVC 构建程序动态链接,记得把和动态库同名的 .lib 后缀的符号导出文件一起拷贝过去,同时在 CMake 里面指定该动态库的导入符号属性文件路径。

CMake 现在已经为 Windows 平台下的函数动态库自动导出所有函数符号实现了预定义宏开关 CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE ,只要该宏被设置了,那么所有函数都可以像 GNU g++/gcc 那样生成的动态库一样自动导出,但是对于这些函数所依赖的全局数据变量,仍然需要使用 __declspec(dllexport/dllimport) 来完成符号的导出和引入,具体可以参考 这篇文章 。

For global data symbols, __declspec(dllimport) must still be used when compiling against the code in the DLL

另外在编译 OpenGL 项目的时候,因为需要用到 glad 来加载 OpenGL 的函数指针,所以我想把 glad.c 以及 glad.h 还有相关的头文件编译为动态库,但是尽管我使用了上述的 CMake 预定义宏,编译能完成但是链接二进制文件的时候,还是报错无法链接到动态库的函数,这困扰了我几个小时,后来在 glad 官方仓库的 issues 里面看到有人回答了这个问题,原来 glad.h 里面对所有函数指针的导出都是以全局公共数据变量而不是函数变量定义的,上文也说到,CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=TRUE 预定义宏只能导出函数符号,全局变量符号还是需要用到 __declspec ,所以对于该动态库的编译,必须在编译的时候添加额外的宏定义参数:

target_compile_definitions(glad PUBLIC GLAD_GLAPI_EXPORT PRIVATE GLAD_GLAPI_EXPORT_BUILD)

因为 glad.h 这个头文件里面定义了这么一段:

#ifndef GLAPI
# if defined(GLAD_GLAPI_EXPORT)
#  if defined(_WIN32) || defined(__CYGWIN__)
#   if defined(GLAD_GLAPI_EXPORT_BUILD)
#    if defined(__GNUC__)
#     define GLAPI __attribute__ ((dllexport)) extern
#    else
#     define GLAPI __declspec(dllexport) extern
#    endif
#   else
#    if defined(__GNUC__)
#     define GLAPI __attribute__ ((dllimport)) extern
#    else
#     define GLAPI __declspec(dllimport) extern
#    endif
#   endif
#  elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)
#   define GLAPI __attribute__ ((visibility ("default"))) extern
#  else
#   define GLAPI extern
#  endif
# else
#  define GLAPI extern
# endif
#endif

真的有点坑人……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/29402.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#开发-集合使用和技巧(六)特殊转换方法SelectMany的介绍和用法

介绍 SelectMany 方法在C#中用于将集合中的元素转换为其他类型的集合&#xff0c;并将这些集合扁平化为一个单一的序列。它是LINQ的一部分&#xff0c;允许你在一个序列上进行投影和过滤操作&#xff0c;然后将结果合并成一个序列。 方法定义 public static IEnumerable<…

城市行人感知新方法:基于音频的行人检测与预测

智慧城市的重要组成部分之一是部署传感器技术来监控和控制城市的各种服务和功能。城市使用各种传感器来评估城市服务的提供和获取方式&#xff0c;这有助于缓解瓶颈问题&#xff0c;并提前预警潜在的服务中断。了解城市服务需求的时间和空间变化有助于更好的资源利用、更公平的…

ionic 项目通过 android studio 打开报错 capacitor.settings.gradle 文件不存在

问题出现 原因分析 在程序相应的目录上面&#xff0c;没有找到对应的配置文件&#xff0c;但是这个文件不是我们自己生成的&#xff0c;而是通过 ionic 编译之后生成。 处理方案 先执行 ionic build&#xff0c;将 ionic 项目打包出来然后执行 npx cap sync 再使用 Android…

Financial Statement Analysis with Large Language Models论文精读

Financial Statement Analysis with Large Language Models 论文精读 文章目录 Financial Statement Analysis with Large Language Models 论文精读Abstract 核心速览研究细节baselineGPT与分析师对比人类分析师与 GPT 的互补性错误预测的来源增量信息增益 分析师出现偏差或分…

【YOLOv10改进[注意力]】在YOLOv10中使用注意力MLCA的实践+ 含全部代码和详细修改方式 + 手撕结构图 + 全网首发

本文将进行在YOLOv10中添加注意力MLCA的实践,助力YOLOv10目标检测效果的实践,文中含全部代码、详细修改方式以及手撕结构图。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 MLCA 二 在YOLOv10中使用注意力MLCA的实践 1 整体修改

【CS.AL】算法核心之分治算法:从入门到进阶

文章目录 1. 概述2. 适用场景3. 设计步骤4. 优缺点5. 典型应用6. 题目和代码示例6.1 简单题目&#xff1a;归并排序6.2 中等题目&#xff1a;最近点对问题6.3 困难题目&#xff1a;分数背包问题 7. 题目和思路表格8. 总结References 1000.01.CS.AL.1.4-核心-DivedeToConquerAlg…

Python兴趣编程百例:手把手带你开发一个图片转字符图的小工具

在数字世界的无尽探索中&#xff0c;我们时常被那些看似平凡的技术所启发&#xff0c;它们如同星辰般点缀着我们的创意天空。今天&#xff0c;我突发奇想&#xff0c;想要用Python开发一个将图片转化为字符画的小工具。这不仅是一次技术的实践&#xff0c;更是一场艺术与科技的…

多客陪玩系统源码支持二次开发陪玩预约系统搭建,打造专业游戏陪玩平台

简述 随着电竞行业的快速发展&#xff0c;电竞陪玩APP正在逐渐成为用户在休闲娱乐时的首选。为了吸引用户和提高用户体验&#xff0c;电竞陪玩APP开发需要定制一些特色功能&#xff0c;并通过合适的盈利模式来获得收益。本文将为您介绍电竞陪玩APP开发需要定制的特色功能以及常…

LiveCharts2:简单灵活交互式且功能强大的.NET图表库

前言 之前的文章中提到过ScottPlot、与oxyplot&#xff0c;这两个是比较常用的.NET图表库&#xff0c;今天介绍一款新的.NET图表库&#xff1a;LiveCharts2。 LiveCharts2介绍 LiveCharts2 是一个现代化的数据可视化库&#xff0c;用于创建动态和交互式图表&#xff0c;支持…

一小时搞定JavaScript(2)——DOM与BOM的应用

前言,本篇文章是依据bilibili博主(波波酱老师)的学习笔记,波波酱老师讲的很好,很适合速成!!! 本篇文章会与java进行对比学习,因为JS中很多语法和java是相同的,所以大家最好熟悉Java语言后再来进行学习,效果更佳,见效更快. 文章目录 5.DOM和BOM5.1 DOM5.1.1传统元素获取5.1.2 C…

高考志愿填报,是选好专业,还是选好学校?过来人给你说说

分数限制下&#xff0c;选好专业还是选好学校&#xff1f; 到底是先选专业还是先选学校&#xff0c;是让考生及家长一直拿不准、辨不清的问题&#xff0c;是优先考虑学校还是专业&#xff0c;上了好学校&#xff0c;专业不喜欢就业前景不理想&#xff0c;怎么办&#xff1f;为…

【未来已来】AI大模型革命:向量数据库如何重塑智能世界?

在人工智能的浪潮中,向量数据库正成为推动AI大模型发展的幕后英雄。这不是简单的技术升级,而是一场关于智能未来的革命。本文将带您深入了解向量数据库如何成为AI大模型的核心竞争力,以及它如何助力我们在智能化的道路上加速前进。 向量数据库:AI大模型的心脏 想象一下…

vue echarts画多柱状图+多折线图

<!--多柱状图折线图--> <div class"echarts-box" id"multiBarPlusLine"></div>import * as echarts from echarts;mounted() {this.getMultiBarPlusLine() },getMultiBarPlusLine() {const container document.getElementById(multiBar…

图书管理系统代码(Java)

1、运行演示 QQ2024528-205028-HD 详细讲解在这篇博客&#xff1a;JavaSE&#xff1a;图书管理系统-CSDN博客 2、所建的包 3、Java代码 3.1 book包 3.1.1 Book类代码 package book;/*** Created with IntelliJ IDEA.* Description:* User: dings* Date: 2024-05-13* Time:…

押注“人类终极能源”!OpenAI与核聚变公司Helion Energy洽谈“购买大量”聚变能源

内容提要 在当下&#xff0c;由 AI 引发的新一轮能源危机已经不再是一个小概率的“黑天鹅”事件&#xff0c;而是一头正在向我们猛冲而来的“灰犀牛”。 文章正文 Helion Energy&#xff0c;是一家总部位于美国华盛顿州埃弗雷特的能源创业公司。 这家成立于 2013 年的公司在…

安卓实现圆形按钮轮廓以及解决无法更改按钮颜色的问题

1.实现按钮轮廓 在drawable文件新建xml文件 <shape xmlns:android"http://schemas.android.com/apk/res/android"<!--实现圆形-->android:shape"oval"><!--指定内部的填充色--><solid android:color"#FFFFFF"/><!-…

【挑战100天首通《谷粒商城》】-【第一天】06、环境-使用vagrant快速创建linux虚拟机

文章目录 课程介绍1、安装 linux 虚拟机2、安装 VirtualBoxStage 1&#xff1a;开启CPU虚拟化Stage 2&#xff1a;下载 VirtualBoxStage 2&#xff1a;安装 VirtualBoxStage 4&#xff1a;安装 VagrantStage 4-1&#xff1a;Vagrant 下载Stage 4-2&#xff1a;Vagrant 安装Stag…

CentOS 7.9上创建JBOD(一)

系列文章目录 CentOS 7.9上创建的JBOD阵列恢复&#xff08;二&#xff09; CentOS 7.9检测硬盘坏区、实物定位&#xff08;三&#xff09; 文章目录 系列文章目录前言一、安装 mdadm工具二、创建JBOD设备三、为JBOD扩容&#xff08;增加一个硬盘&#xff09;四、最后&#xff…

MySQL修改用户权限(宝塔)

在我们安装好的MySQL中&#xff0c;很可能对应某些操作时&#xff0c;不具备操作的权限&#xff0c;如下是解决这些问题的方法 我以宝塔创建数据库为例&#xff0c;创建完成后&#xff0c;以创建的用户名和密码登录 这里宝塔中容易发生问题的地方&#xff0c;登录不上去&#…

STM32单片机-通信协议(下)

STM32单片机-通信协议(下&#xff09; 一、通信协议介绍二、USART(通用同步/异步收发器)2.1 USART框图和基本结构2.2 串口发送2.2.1 Printf函数移植2.2.2 串口发送汉字 2.3 串口接收2.3.1 串口接收查询2.3.2 串口接收中断 2.4 USART串口数据包2.4.1 数据包格式2.4.2 数据包接收…