video | code |
---|---|
https://www.youtube.com/watch?v=4O0_-1NaWnY,https://www.bilibili.com/video/BV1oj411p7qM/? | https://github.com/jdah/minecraft-weekend |
项目依赖
在构建主项目之前,必须构建以下 lib/ 静态库:
– | ||
---|---|---|
GLAD | lib/glad/src/glad.o | |
CGLM | 高度优化的 C 图形数学 (glm) | lib/cglm/.libs/libcglm.a |
GLFW | 一个开源多平台库,适用于 OpenGL、OpenGL ES 和 桌面上的 Vulkan 开发。它提供了一个简单的API来创建 窗口、上下文和表面,接收输入和事件。GLFW 采用 C 语言编写,支持 Windows、macOS、X11 和 Wayland。 | lib/glfw/src/libglfw3.a |
libnoise | lib/noise/libnoise.a |
VAO(Vertex Array Object)和VBO(Vertex Buffer Object)
在图形编程中,特别是使用OpenGL或类似图形API的情况下,VAO(Vertex Array Object)和VBO(Vertex Buffer Object)是用于管理顶点数据的重要概念。
-
VBO(Vertex Buffer Object):
- 作用:VBO是用来存储顶点数据的缓冲区对象。顶点数据包括顶点坐标、颜色、法线等信息。
- 使用场景:通常,一个VBO包含所有的顶点数据,以便能够高效地将它们传递给GPU。
- 好处:使用VBO能够减少CPU和GPU之间的数据传输次数,提高性能。
-
VAO(Vertex Array Object):
- 作用:VAO是用于封装和存储顶点数组状态的对象。它保存了顶点缓冲区对象(VBO)的状态,以及顶点属性指针、顶点索引等。
- 使用场景:当你有多个VBO时,可以使用VAO将它们组织起来,方便地进行状态切换。
- 好处:VAO的使用可以帮助你更高效地管理和维护多个顶点数组对象。
简而言之,VBO用于存储实际的顶点数据,而VAO用于存储渲染时需要的状态和指针信息。在OpenGL中,使用它们的一般流程是:
- 创建并绑定一个VAO。
- 在VAO中启用和配置顶点属性指针,包括启用VBO。
- 创建并绑定一个或多个VBO,将顶点数据传递给它们。
- 解绑VAO和VBO,以避免意外修改。
这种结构的使用能够有效地减少不必要的状态切换,提高图形渲染的效率。在现代OpenGL中,使用VAO和VBO已经成为标配,尤其是在处理大量顶点数据时。
纹理图集
纹理图集(Texture Atlas)是一种将多个小纹理图合并到一个大纹理中的技术。这种技术的目的是减少图形渲染中的资源开销,提高渲染效率。通常,这个大纹理包含了游戏或应用程序中需要使用的多个小纹理,这些小纹理可以是角色、场景元素、GUI元素等。
以下是纹理图集的一些主要优势和用途:
-
减少渲染调用:使用一个大的纹理图集,而不是多个小纹理,可以减少渲染调用的次数。这有助于提高渲染效率,特别是在图形API中存在批处理机制的情况下。
-
减少内存占用:合并小纹理到一个图集中可以减少内存的占用,因为每个小纹理都需要一定的内存。这对于移动设备和性能受限的环境尤为重要。
-
减少纹理切换:在图集中,所有的纹理都在同一个大纹理中,减少了纹理切换的需求。这对于图形渲染中的状态切换来说是一项优化。
-
提高纹理缓存命中率:当纹理数据紧密打包在一起时,有助于提高纹理缓存的命中率,减少纹理读取的性能损失。
使用纹理图集的一般步骤包括:
-
创建纹理图集:将所有小纹理合并到一个大纹理中。这可以手动完成,也可以使用专门的工具进行自动打包。
-
纹理坐标调整:在渲染过程中,需要相应地调整纹理坐标,以正确地从纹理图集中获取所需的小纹理。
-
优化纹理加载:在加载纹理图集时,可以选择加载整个图集,也可以只加载在当前场景或关卡中需要的部分。这有助于减少内存占用。
纹理图集是游戏和图形应用程序中常见的优化技术之一,可以显著提高性能和资源利用率。
分块系统
“Minecraft”(我的世界)是一款采用分块系统的开放世界沙盒游戏。分块系统是指将游戏世界划分为小块,每个块包含一定区域的游戏世界信息。这种设计使得游戏能够高效地处理大规模的世界,同时节省内存和处理资源。以下是关于"Minecraft"分块系统的一些基本概念:
-
块(Chunk):
- "Minecraft"中的世界被分割成块,每个块是一个立方体,通常边长为16个方块。
- 块是世界的基本单位,包含了地形、生物、植被等信息。
- 块的边界对齐,这有助于简化坐标运算和优化渲染。
-
块的加载与卸载:
- 仅加载玩家周围的块,以减小资源开销。当玩家移动时,系统会加载新的块,同时卸载远离玩家的块。
- 这种动态加载和卸载块的机制有助于维持良好的性能。
-
渲染优化:
- 只渲染可见块,减少渲染负担。
- 使用视锥体剔除(frustum culling)技术,仅渲染玩家视野内的块。
-
存储与持久性:
- 块数据通常被持久化存储,以确保玩家的建筑、物品等信息在游戏重启时不会丢失。
- 存储格式可以采用压缩算法以减小存储空间占用。
-
世界生成:
- 初始时,游戏会生成一个包含各种地形特征的世界。
- 地形生成算法通常基于特定的种子(seed),以确保相同的种子生成相同的世界。
-
事件驱动的更新:
- 分块系统可以通过事件驱动机制实现,即只在需要时更新块。
- 当玩家破坏方块、放置方块或世界发生其他变化时,只需更新受影响的块,而不是整个世界。
"Minecraft"的分块系统是其成功的关键之一,它为玩家提供了庞大而丰富的世界,同时通过精巧的设计保证了游戏的性能和可扩展性。
地形生成
poppin效应
“Popping” 或 “Poppin’ effect” 通常用于描述当对象或元素在图形渲染中突然出现或消失时产生的视觉效果。这种效应通常在计算机图形学和游戏开发中出现,特别是在对象的可见性状态改变时。
具体而言,poppin效应可能表现为以下情况:
-
突然出现(Pop In):当一个对象在场景中突然变得可见时,而不是平滑地淡入,观察者可能感到突兀。这通常发生在对象从远处逐渐变得可见的过程中,而不是平滑地渐显。
-
突然消失(Pop Out):与突然出现相对,突然消失是指一个对象在场景中突然变得不可见,而不是平滑地淡出。这可能在对象离开视野时发生。
-
LOD(Level of Detail)效应:在使用LOD技术时,当系统从低细节级别切换到高细节级别时,可能产生突兀的效果。这种情况下,高细节级别的模型或纹理在瞬间替代了低细节级别的,从而导致了poppin效应。
"Poppin"效应通常是视觉上的不愉快,因为它会破坏场景的连续性,使渲染的场景看起来不够平滑或自然。为了减轻或避免这种效应,开发者可能会采用一些技术,例如:
-
淡入淡出效果:在对象的可见性状态发生变化时,通过应用淡入淡出效果,可以使过渡更加平滑。
-
渐进细节切换:在使用LOD技术时,可以考虑在切换不同细节级别时逐渐引入高细节级别,而不是突然切换。
-
过渡区域:在对象变得可见或不可见的区域,可以使用过渡区域,渐变性地引入或淡出对象,以减缓效果。
通过这些技术,可以提高视觉效果的流畅性,提供更自然的渲染体验,减少或消除poppin效应。
MAKE
git clone --recurse-submodules https://github.com/jdah/minecraft-weekend.git
cd minecraft-weekend/
make
// 在运行make命令时没有指定目标名称,默认情况下,make将执行第一个出现的目标。在给定的Makefile中,第一个目标是all,它将执行all目标,会构建目标dirs、libs和game。还可以通过设置.DEFAULT_GOAL的值改变默认行为。
# Determine the operating system
UNAME_S = $(shell uname -s) # 通过 `uname -s` 命令获取当前操作系统的名称
# Compiler and Compiler Flags
CC = clang # 指定C语言编译器为clang ,CFLAGS:编译器标志,用于设置编译器的各种选项,包括标准、优化级别、警告等
CFLAGS = -std=c11 -O3 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing
CFLAGS += -Wno-pointer-arith -Wno-newline-eof -Wno-unused-parameter -Wno-gnu-statement-expression
CFLAGS += -Wno-gnu-compound-literal-initializer -Wno-gnu-zero-variadic-macro-arguments
CFLAGS += -Ilib/cglm/include -Ilib/glad/include -Ilib/glfw/include -Ilib/stb -Ilib/noise -fbracket-depth=1024
# Linker Flags 链接器标志,用于指定链接时需要的库文件和其他标志
LDFLAGS = lib/glad/src/glad.o lib/cglm/libcglm.a lib/glfw/src/libglfw3.a lib/noise/libnoise.a -lm
# Additional Frameworks on macOS
ifeq ($(UNAME_S), Darwin)
LDFLAGS += -framework OpenGL -framework IOKit -framework CoreVideo -framework Cocoa
endif
# Additional Libraries on Linux
ifeq ($(UNAME_S), Linux)
LDFLAGS += -ldl -lpthread
endif
# Find source files 通过通配符查找所有的C源文件
SRC = $(wildcard src/**/*.c) $(wildcard src/*.c) $(wildcard src/**/**/*.c) $(wildcard src/**/**/**/*.c)
# Generate object file names from source files 通过将源文件的扩展名从`.c`更改为`.o`,生成目标文件的列表
OBJ = $(SRC:.c=.o)
# Output directory 指定生成可执行文件的目录
BIN = bin
# Phony targets (targets that are not associated with files) 伪目标
.PHONY: all clean
# Default target builds everything
all: dirs libs game
# Build libraries
libs:
cd lib/cglm && cmake . -DCGLM_STATIC=ON && make
cd lib/glad && $(CC) -o src/glad.o -Iinclude -c src/glad.c
cd lib/glfw && cmake . && make
cd lib/noise && make
# Create necessary directories
dirs:
mkdir -p ./$(BIN)
# Run the executable
run: all
$(BIN)/game
# Build the final executable
game: $(OBJ)
$(CC) -o $(BIN)/game $^ $(LDFLAGS)
# Compile source files to object files
%.o: %.c
$(CC) -o $@ -c $< $(CFLAGS)
# Clean up generated files
clean:
rm -rf $(BIN) $(OBJ)