网站型和商城型有什么区别网页制作价格

张小明 2026/1/1 6:13:19
网站型和商城型有什么区别,网页制作价格,ui设计的就业前景,wordpress说说分类上一篇#xff1a;多渲染通道 | 下一篇#xff1a;方向光照 | 返回目录 #x1f4da; 快速导航 目录 简介学习目标2D顶点格式 vertex_2d定义与vertex_3d的区别顶点布局对比 材质类型扩展 材质类型枚举UI材质配置材质加载器更新 泛型几何体创建 接口修改顶点大小参数Vulkan后…上一篇多渲染通道 | 下一篇方向光照 | 返回目录 快速导航目录简介学习目标2D顶点格式vertex_2d定义与vertex_3d的区别顶点布局对比材质类型扩展材质类型枚举UI材质配置材质加载器更新泛型几何体创建接口修改顶点大小参数Vulkan后端适配默认2D几何体创建默认2D Quad2D坐标系统UI几何体创建定义UI顶点几何体配置从配置获取几何体渲染流程集成准备Render PacketUI几何体绘制坐标空间转换常见问题练习 简介在上一教程中,我们实现了多渲染通道架构,将 World Renderpass 和 UI Renderpass 分离。现在我们将实现在 UI Renderpass 中实际绘制 2D UI 元素。要在 UI Renderpass 中绘制,我们需要:2D 顶点格式(vertex_2d):只包含位置和纹理坐标的简化顶点UI 材质类型:区分 3D 世界材质和 2D UI 材质泛型几何体创建:支持不同顶点格式的统一接口默认 2D 几何体:用于测试的基础 2D quadRenderpasses 渲染通道Geometry Creation 几何体创建Material Types 材质类型Vertex Formats 顶点格式World Renderpass世界渲染UI RenderpassUI渲染create_geometry泛型接口MATERIAL_TYPE_WORLD世界材质MATERIAL_TYPE_UIUI材质vertex_3d3D顶点vertex_2d2D顶点 学习目标目标描述定义2D顶点格式实现简化的 vertex_2d 结构扩展材质类型区分 WORLD 和 UI 材质泛型几何体创建支持多种顶点格式的统一接口创建默认2D几何体实现默认的 2D quadUI几何体渲染在 UI renderpass 中绘制 2D 几何体 2D顶点格式vertex_2d定义2D UI 元素不需要 3D 渲染的所有数据:// engine/src/math/math_types.h/** * brief 3D 顶点结构 (用于世界几何体) */typedefstructvertex_3d{vec3 position;// 3D 位置 (12 bytes)vec2 texcoord;// 纹理坐标 (8 bytes)vec3 normal;// 法线 (12 bytes)vec3 tangent;// 切线 (12 bytes)// 总共: 44 bytes}vertex_3d;/** * brief 2D 顶点结构 (用于 UI 几何体) */typedefstructvertex_2d{vec2 position;// 2D 位置 (8 bytes)vec2 texcoord;// 纹理坐标 (8 bytes)// 总共: 16 bytes}vertex_2d;与vertex_3d的区别对比两种顶点格式:属性vertex_3dvertex_2d说明positionvec3(x, y, z)vec2(x, y)2D 只需 x, ytexcoordvec2vec2纹理坐标相同normalvec3❌UI 不需要法线tangentvec3❌UI 不需要切线大小44 bytes16 bytes2D 节省 64% 内存顶点布局对比vertex_3d 内存布局 (44 bytes): ┌─────────────────────────────────────────────┐ │ position (vec3) │ 12 bytes │ ├────────────────────────┼────────────────────┤ │ texcoord (vec2) │ 8 bytes │ ├────────────────────────┼────────────────────┤ │ normal (vec3) │ 12 bytes │ ├────────────────────────┼────────────────────┤ │ tangent (vec3) │ 12 bytes │ └─────────────────────────────────────────────┘ vertex_2d 内存布局 (16 bytes): ┌─────────────────────────────────────────────┐ │ position (vec2) │ 8 bytes │ ├────────────────────────┼────────────────────┤ │ texcoord (vec2) │ 8 bytes │ └─────────────────────────────────────────────┘ 优势: ✓ 更小的顶点缓冲区 ✓ 更少的带宽占用 ✓ 更快的顶点着色器 ✓ 简化的顶点属性设置Vulkan 顶点输入描述:// 3D 顶点输入VkVertexInputAttributeDescription vertex_3d_attributes[4];vertex_3d_attributes[0].location0;vertex_3d_attributes[0].formatVK_FORMAT_R32G32B32_SFLOAT;// position (vec3)vertex_3d_attributes[0].offsetoffsetof(vertex_3d,position);vertex_3d_attributes[1].location1;vertex_3d_attributes[1].formatVK_FORMAT_R32G32_SFLOAT;// texcoord (vec2)vertex_3d_attributes[1].offsetoffsetof(vertex_3d,texcoord);vertex_3d_attributes[2].location2;vertex_3d_attributes[2].formatVK_FORMAT_R32G32B32_SFLOAT;// normal (vec3)vertex_3d_attributes[2].offsetoffsetof(vertex_3d,normal);vertex_3d_attributes[3].location3;vertex_3d_attributes[3].formatVK_FORMAT_R32G32B32_SFLOAT;// tangent (vec3)vertex_3d_attributes[3].offsetoffsetof(vertex_3d,tangent);// 2D 顶点输入 (简化)VkVertexInputAttributeDescription vertex_2d_attributes[2];vertex_2d_attributes[0].location0;vertex_2d_attributes[0].formatVK_FORMAT_R32G32_SFLOAT;// position (vec2)vertex_2d_attributes[0].offsetoffsetof(vertex_2d,position);vertex_2d_attributes[1].location1;vertex_2d_attributes[1].formatVK_FORMAT_R32G32_SFLOAT;// texcoord (vec2)vertex_2d_attributes[1].offsetoffsetof(vertex_2d,texcoord); 材质类型扩展材质类型枚举为了区分 3D 世界材质和 2D UI 材质,我们添加了材质类型枚举:// engine/src/resources/resource_types.h/** * brief 材质类型 */typedefenummaterial_type{MATERIAL_TYPE_WORLD,// 世界材质 (3D)MATERIAL_TYPE_UI// UI 材质 (2D)}material_type;typedefstructmaterial{u32 id;u32 generation;u32 internal_id;material_type type;// ← 新增:材质类型charname[MATERIAL_NAME_MAX_LENGTH];vec4 diffuse_colour;texture_map diffuse_map;}material;typedefstructmaterial_config{charname[MATERIAL_NAME_MAX_LENGTH];material_type type;// ← 新增:材质类型b8 auto_release;vec4 diffuse_colour;chardiffuse_map_name[TEXTURE_NAME_MAX_LENGTH];}material_config;UI材质配置UI 材质配置文件示例:# assets/materials/test_ui_material.kmt # 材质版本 version0.1 # 材质名称 nametest_ui_material # 漫反射颜色 (RGBA) diffuse_colour1.0 1.0 1.0 1.0 # 漫反射贴图名称 diffuse_map_nameorange_lines_512 # 材质类型:ui 或 world typeui对比世界材质和 UI 材质:世界材质 (test_material.kmt): version0.1 nametest_material diffuse_colour1.0 1.0 1.0 1.0 diffuse_map_namepaving typeworld ← 3D 世界材质 UI 材质 (test_ui_material.kmt): version0.1 nametest_ui_material diffuse_colour1.0 1.0 1.0 1.0 diffuse_map_nameorange_lines_512 typeui ← 2D UI 材质材质加载器更新材质加载器需要解析type字段:// engine/src/resources/loaders/material_loader.cstaticb8material_loader_load(resource_loader*self,constchar*name,resource*out_resource){// ... 打开文件、读取配置 ...// 设置默认值material_config*resource_datakallocate(sizeof(material_config),MEMORY_TAG_MATERIAL);resource_data-typeMATERIAL_TYPE_WORLD;// 默认为世界材质resource_data-auto_releasefalse;string_ncopy(resource_data-name,name,MATERIAL_NAME_MAX_LENGTH);resource_data-diffuse_colourvec4_one();kzero_memory(resource_data-diffuse_map_name,TEXTURE_NAME_MAX_LENGTH);// 逐行解析while(filesystem_read_line(f,511,p,line_length)){char*trimmedstring_trim(line_buf);// 跳过注释和空行if(line_length1||trimmed[0]#){continue;}// 解析 keyvaluei32 equal_indexstring_index_of(trimmed,);if(equal_index-1){continue;}charkey[64];string_mid(key,trimmed,0,equal_index);char*key_trimmedstring_trim(key);charvalue[512];string_mid(value,trimmed,equal_index1,-1);char*value_trimmedstring_trim(value);// 解析具体字段if(strings_equali(key_trimmed,version)){// 版本号}elseif(strings_equali(key_trimmed,name)){string_ncopy(resource_data-name,value_trimmed,MATERIAL_NAME_MAX_LENGTH);}elseif(strings_equali(key_trimmed,diffuse_map_name)){string_ncopy(resource_data-diffuse_map_name,value_trimmed,TEXTURE_NAME_MAX_LENGTH);}elseif(strings_equali(key_trimmed,diffuse_colour)){if(!string_to_vec4(value_trimmed,resource_data-diffuse_colour)){KWARN(Failed to parse diffuse_colour);}}elseif(strings_equali(key_trimmed,type)){// 新增:解析材质类型 if(strings_equali(value_trimmed,ui)){resource_data-typeMATERIAL_TYPE_UI;}elseif(strings_equali(value_trimmed,world)){resource_data-typeMATERIAL_TYPE_WORLD;}else{KWARN(Unknown material type %s, defaulting to world,value_trimmed);resource_data-typeMATERIAL_TYPE_WORLD;}}}filesystem_close(f);// 填充 resourceout_resource-full_pathstring_duplicate(full_path);out_resource-namename;out_resource-dataresource_data;out_resource-data_sizesizeof(material_config);out_resource-loader_idself-id;returntrue;} 泛型几何体创建接口修改为了支持不同的顶点格式 (vertex_3d, vertex_2d),我们将create_geometry接口改为泛型:// engine/src/renderer/renderer_types.inltypedefstructrenderer_backend{// ... 其他函数 ...// 旧接口 (只支持 vertex_3d) // b8 (*create_geometry)(geometry* geometry, u32 vertex_count, const vertex_3d* vertices, u32 index_count, const u32* indices);// 新接口 (支持任意顶点格式) b8(*create_geometry)(geometry*geometry,u32 vertex_size,// ← 新增:顶点大小 (字节)u32 vertex_count,// 顶点数量constvoid*vertices,// ← 改为 void* (泛型指针)u32 index_size,// ← 新增:索引大小 (字节)u32 index_count,// 索引数量constvoid*indices// ← 改为 void* (泛型指针));void(*destroy_geometry)(geometry*geometry);}renderer_backend;顶点大小参数为什么需要vertex_size参数?// 使用旧接口 (固定类型)vertex_3d vertices[4];// ...backend.create_geometry(geometry,4,vertices,6,indices);// 问题:只能传递 vertex_3d*,无法支持其他顶点格式// 使用新接口 (泛型)vertex_2d ui_vertices[4];// ...backend.create_geometry(geometry,sizeof(vertex_2d),// ← 明确告诉后端顶点大小4,ui_vertices,// ← void* 可以接受任意类型sizeof(u32),6,indices);vertex_3d world_vertices[100];// ...backend.create_geometry(geometry,sizeof(vertex_3d),// ← 不同的顶点大小100,world_vertices,sizeof(u32),300,indices);优势:泛型接口的优势: ┌────────────────────────────────────┐ │ Renderer Backend (渲染后端) │ │ │ │ create_geometry(vertex_size, ...) │ └─────────────┬──────────────────────┘ │ │ 接受任意顶点格式 │ ┌─────────┼─────────┐ │ │ │ ▼ ▼ ▼ ┌───────┐ ┌───────┐ ┌───────┐ │vertex │ │vertex │ │custom │ │_3d │ │_2d │ │_vertex│ └───────┘ └───────┘ └───────┘ 44 bytes 16 bytes 任意大小 后端计算缓冲区大小: buffer_size vertex_size * vertex_countVulkan后端适配Vulkan 后端的实现:// engine/src/renderer/vulkan/vulkan_backend.cb8vulkan_renderer_create_geometry(geometry*geometry,u32 vertex_size,u32 vertex_count,constvoid*vertices,u32 index_size,u32 index_count,constvoid*indices){if(!vertex_count||!vertices){KERROR(vulkan_renderer_create_geometry requires vertex data, and none was supplied. vertex_count%d, vertices%p,vertex_count,vertices);returnfalse;}// 检查是否有索引数据b8 is_indexedindex_count0indices!0;// 计算缓冲区大小u64 vertex_buffer_sizevertex_size*vertex_count;u64 index_buffer_sizeis_indexed?(index_size*index_count):0;// 存储几何体数据vulkan_geometry_data*internal_datacontext.geometries[geometry-internal_id];internal_data-idgeometry-id;internal_data-generationgeometry-generation;internal_data-vertex_countvertex_count;internal_data-vertex_sizevertex_size;// ← 保存顶点大小internal_data-vertex_buffer_offsetcontext.geometry_vertex_offset;internal_data-index_countindex_count;internal_data-index_sizeindex_size;// ← 保存索引大小internal_data-index_buffer_offsetcontext.geometry_index_offset;// 上传顶点数据到 GPUvulkan_buffer_load_range(context,context.object_vertex_buffer,internal_data-vertex_buffer_offset,vertex_buffer_size,vertices// ← void* 可以接受任意类型);// 上传索引数据到 GPUif(is_indexed){vulkan_buffer_load_range(context,context.object_index_buffer,internal_data-index_buffer_offset,index_buffer_size,indices);}// 更新偏移量context.geometry_vertex_offsetvertex_buffer_size;if(is_indexed){context.geometry_index_offsetindex_buffer_size;}returntrue;} 默认2D几何体创建默认2D Quad几何体系统创建两个默认几何体:3D quad 和 2D quad。// engine/src/systems/geometry_system.ctypedefstructgeometry_system_state{geometry_system_config config;geometry default_geometry;// 3D 默认几何体geometry default_2d_geometry;// ← 新增:2D 默认几何体geometry_reference*registered_geometries;}geometry_system_state;b8create_default_geometries(geometry_system_state*state){// 创建默认 3D 几何体 vertex_3d verts[4];kzero_memory(verts,sizeof(vertex_3d)*4);constf32 f10.0f;verts[0].position.x-0.5*f;// 0 3verts[0].position.y-0.5*f;//verts[0].texcoord.x0.0f;//verts[0].texcoord.y0.0f;// 2 1verts[1].position.y0.5*f;verts[1].position.x0.5*f;verts[1].texcoord.x1.0f;verts[1].texcoord.y1.0f;verts[2].position.x-0.5*f;verts[2].position.y0.5*f;verts[2].texcoord.x0.0f;verts[2].texcoord.y1.0f;verts[3].position.x0.5*f;verts[3].position.y-0.5*f;verts[3].texcoord.x1.0f;verts[3].texcoord.y0.0f;u32 indices[6]{0,1,2,0,3,1};// 上传到 GPU (使用泛型接口)if(!renderer_create_geometry(state-default_geometry,sizeof(vertex_3d),// ← 顶点大小4,verts,sizeof(u32),6,indices)){KFATAL(Failed to create default geometry. Application cannot continue.);returnfalse;}state-default_geometry.materialmaterial_system_get_default();// 创建默认 2D 几何体 vertex_2d verts2d[4];kzero_memory(verts2d,sizeof(vertex_2d)*4);verts2d[0].position.x-0.5*f;// 0 3verts2d[0].position.y-0.5*f;//verts2d[0].texcoord.x0.0f;//verts2d[0].texcoord.y0.0f;// 2 1verts2d[1].position.y0.5*f;verts2d[1].position.x0.5*f;verts2d[1].texcoord.x1.0f;verts2d[1].texcoord.y1.0f;verts2d[2].position.x-0.5*f;verts2d[2].position.y0.5*f;verts2d[2].texcoord.x0.0f;verts2d[2].texcoord.y1.0f;verts2d[3].position.x0.5*f;verts2d[3].position.y-0.5*f;verts2d[3].texcoord.x1.0f;verts2d[3].texcoord.y0.0f;// 上传 2D 几何体到 GPUif(!renderer_create_geometry(state-default_2d_geometry,sizeof(vertex_2d),// ← 2D 顶点大小 (16 bytes)4,verts2d,// ← vertex_2d 数组sizeof(u32),6,indices)){KFATAL(Failed to create default 2D geometry. Application cannot continue.);returnfalse;}state-default_2d_geometry.materialmaterial_system_get_default();returntrue;}// 获取默认 3D 几何体geometry*geometry_system_get_default(){if(state_ptr){returnstate_ptr-default_geometry;}KFATAL(geometry_system_get_default called before system was initialized. Returning nullptr.);return0;}// 获取默认 2D 几何体geometry*geometry_system_get_default_2d(){if(state_ptr){returnstate_ptr-default_2d_geometry;}KFATAL(geometry_system_get_default_2d called before system was initialized. Returning nullptr.);return0;}2D坐标系统默认 2D quad 的顶点坐标:2D Quad 顶点布局: ┌────────────────────┐ │ (-5, -5) (5, -5)│ ← 顶点 0 和 3 │ 0 3 │ │ │ │ │ │ │ │ 2 1 │ │ (-5, 5) (5, 5)│ ← 顶点 2 和 1 └────────────────────┘ 纹理坐标映射: ┌────────────────────┐ │ (0,0) (1,0) │ ← 顶点 0 和 3 │ 0 3 │ │ │ │ │ │ │ │ 2 1 │ │ (0,1) (1,1) │ ← 顶点 2 和 1 └────────────────────┘ 索引顺序 (逆时针): Triangle 1: 0 → 1 → 2 Triangle 2: 0 → 3 → 1 UI几何体创建定义UI顶点在应用层创建 UI 几何体:// engine/src/core/application.c// 定义 UI 几何体配置geometry_config ui_config;ui_config.vertex_sizesizeof(vertex_2d);// ← 2D 顶点大小ui_config.vertex_count4;ui_config.index_sizesizeof(u32);ui_config.index_count6;string_ncopy(ui_config.material_name,test_ui_material,MATERIAL_NAME_MAX_LENGTH);string_ncopy(ui_config.name,test_ui_geometry,GEOMETRY_NAME_MAX_LENGTH);// 创建 512x512 的 UI quadconstf32 f512.0f;vertex_2d uiverts[4];uiverts[0].position.x0.0f;// 0 3uiverts[0].position.y0.0f;//uiverts[0].texcoord.x0.0f;//uiverts[0].texcoord.y0.0f;// 2 1uiverts[1].position.yf;uiverts[1].position.xf;uiverts[1].texcoord.x1.0f;uiverts[1].texcoord.y1.0f;uiverts[2].position.x0.0f;uiverts[2].position.yf;uiverts[2].texcoord.x0.0f;uiverts[2].texcoord.y1.0f;uiverts[3].position.xf;uiverts[3].position.y0.0f;uiverts[3].texcoord.x1.0f;uiverts[3].texcoord.y0.0f;ui_config.verticesuiverts;// 索引 (逆时针)u32 uiindices[6]{2,1,0,3,0,1};ui_config.indicesuiindices;顶点布局可视化:UI Quad (512x512 像素): ┌─────────────────────┐ (512, 0) │ (0, 0) 3 │ │ 0 │ │ │ │ │ │ │ │ │ │ 2 1 │ │ (512, 512) └─────────────────────┘ 屏幕坐标系: (0, 0) ───────► X (向右) │ │ ▼ Y (向下) 模型矩阵变换后: model mat4_translation((vec3){100, 100, 0}) 最终屏幕位置: (100, 100) 到 (612, 612)几何体配置geometry_config结构用于配置几何体:// engine/src/systems/geometry_system.htypedefstructgeometry_config{u32 vertex_size;// 顶点大小 (字节)u32 vertex_count;// 顶点数量void*vertices;// 顶点数据指针u32 index_size;// 索引大小 (字节)u32 index_count;// 索引数量void*indices;// 索引数据指针charname[GEOMETRY_NAME_MAX_LENGTH];// 几何体名称charmaterial_name[MATERIAL_NAME_MAX_LENGTH];// 材质名称}geometry_config;从配置获取几何体使用配置创建几何体:// engine/src/core/application.c// 从配置获取 UI 几何体app_state-test_ui_geometrygeometry_system_acquire_from_config(ui_config,true);if(!app_state-test_ui_geometry){KERROR(Failed to acquire UI geometry);returnfalse;}geometry_system_acquire_from_config的实现:// engine/src/systems/geometry_system.cgeometry*geometry_system_acquire_from_config(geometry_config config,b8 auto_release){geometry*g0;// 1. 查找空闲槽位for(u32 i0;istate_ptr-config.max_geometry_count;i){if(state_ptr-registered_geometries[i].geometry.idINVALID_ID){// 找到空闲槽位state_ptr-registered_geometries[i].auto_releaseauto_release;state_ptr-registered_geometries[i].reference_count1;gstate_ptr-registered_geometries[i].geometry;g-idi;break;}}if(!g){KERROR(Unable to obtain free slot for geometry. Adjust configuration to allow more space.);return0;}// 2. 创建几何体 (上传到 GPU)if(!create_geometry(state_ptr,config,g)){KERROR(Failed to create geometry. Returning nullptr.);return0;}returng;}b8create_geometry(geometry_system_state*state,geometry_config config,geometry*g){// 上传到 GPUif(!renderer_create_geometry(g,config.vertex_size,config.vertex_count,config.vertices,config.index_size,config.index_count,config.indices)){// 创建失败,清理state-registered_geometries[g-id].reference_count0;state-registered_geometries[g-id].auto_releasefalse;g-idINVALID_ID;g-generationINVALID_ID;g-internal_idINVALID_ID;returnfalse;}// 获取材质if(string_length(config.material_name)0){g-materialmaterial_system_acquire(config.material_name);if(!g-material){g-materialmaterial_system_get_default();}}returntrue;} 渲染流程集成准备Render Packet在应用主循环中准备 render packet:// engine/src/core/application.cb8application_run(){// ... 主循环 ...while(app_state-is_running){// ... 更新逻辑 ...// 准备 Render Packet render_packet packet;packet.delta_timedelta;// 3D 世界几何体geometry_render_data test_world_render;test_world_render.geometryapp_state-test_geometry;test_world_render.modelmat4_identity();packet.geometry_count1;packet.geometriestest_world_render;// 2D UI 几何体geometry_render_data test_ui_render;test_ui_render.geometryapp_state-test_ui_geometry;test_ui_render.modelmat4_translation((vec3){0,0,0});// 屏幕左上角packet.ui_geometry_count1;packet.ui_geometriestest_ui_render;// 提交渲染renderer_draw_frame(packet);// ...}returntrue;}UI几何体绘制渲染器前端处理 UI 几何体:// engine/src/renderer/renderer_frontend.cb8renderer_draw_frame(render_packet*packet){// 开始帧if(state_ptr-backend.begin_frame(state_ptr-backend,packet-delta_time)){// World Renderpass backend.begin_renderpass(BUILTIN_RENDERPASS_WORLD);backend.update_global_world_state(projection,view,...);// 绘制世界几何体u32 countpacket-geometry_count;for(u32 i0;icount;i){backend.draw_geometry(packet-geometries[i]);}backend.end_renderpass(BUILTIN_RENDERPASS_WORLD);// UI Renderpass backend.begin_renderpass(BUILTIN_RENDERPASS_UI);backend.update_global_ui_state(ui_projection,ui_view,0);// 绘制 UI 几何体countpacket-ui_geometry_count;for(u32 i0;icount;i){backend.draw_geometry(packet-ui_geometries[i]);// ← 使用 vertex_2d}backend.end_renderpass(BUILTIN_RENDERPASS_UI);// 结束帧b8 resultbackend.end_frame(state_ptr-backend,packet-delta_time);backend.frame_number;if(!result){KERROR(renderer_end_frame failed. Application shutting down...);returnfalse;}}returntrue;} 坐标空间转换UI 几何体的坐标空间转换:1. 本地空间 (Local Space): 顶点定义: (0, 0) 到 (512, 512) ┌────────┐ │ │ 512x512 quad └────────┘ 2. 模型变换 (Model Transform): model mat4_translation((vec3){100, 100, 0}) ┌────────┐ │ │ 移动到 (100, 100) └────────┘ 3. 视图变换 (View Transform): view mat4_identity() (UI 无相机) ┌────────┐ │ │ 无变化 └────────┘ 4. 投影变换 (Projection Transform): projection mat4_orthographic(0, 1280, 720, 0, -100, 100) ┌────────┐ │ │ 映射到 NDC [-1, 1] └────────┘ 5. 视口变换 (Viewport Transform): NDC → 屏幕坐标 (0, 0) 到 (1280, 720) ┌────────┐ │ │ 最终显示在屏幕 (100, 100) 到 (612, 612) └────────┘完整变换管道:// UI 顶点着色器中的变换voidmain(){// 本地空间顶点vec2 local_posin_position;// (0, 0) ~ (512, 512)// 应用模型矩阵 (平移到屏幕位置)vec4 world_posu_push_constants.model*vec4(local_pos,0.0,1.0);// world_pos (100, 100, 0, 1)// 应用视图矩阵 (UI 通常是单位矩阵)vec4 view_posglobal_ubo.view*world_pos;// view_pos (100, 100, 0, 1)// 应用正交投影矩阵vec4 clip_posglobal_ubo.projection*view_pos;// clip_pos NDC 坐标gl_Positionclip_pos;}❓ 常见问题1. 为什么需要 vertex_2d?直接用 vertex_3d 设置 z0 不行吗?技术上可行,但不推荐:// 方案 1:使用 vertex_3d (浪费)vertex_3d ui_verts[4];ui_verts[0].position(vec3){0,0,0};// z0ui_verts[0].texcoord(vec2){0,0};ui_verts[0].normal(vec3){0,0,1};// ← 浪费:UI 不需要法线ui_verts[0].tangent(vec3){1,0,0};// ← 浪费:UI 不需要切线// 内存占用:44 bytes * 4 176 bytes// 方案 2:使用 vertex_2d (优化)vertex_2d ui_verts[4];ui_verts[0].position(vec2){0,0};ui_verts[0].texcoord(vec2){0,0};// 内存占用:16 bytes * 4 64 bytes (节省 64%)为什么 vertex_2d 更好:内存效率: 节省 64% 内存 (44 bytes → 16 bytes)带宽效率: 减少 GPU 内存带宽占用性能: 更少的数据传输,更快的顶点着色器语义清晰: 明确表示这是 2D 几何体管线优化: 可以为 2D 几何体创建专门的优化管线实际影响 (1000 个 UI 元素):使用 vertex_3d: - 顶点缓冲区: 44 bytes * 4 * 1000 176 KB - 索引缓冲区: 4 bytes * 6 * 1000 24 KB - 总计: 200 KB 使用 vertex_2d: - 顶点缓冲区: 16 bytes * 4 * 1000 64 KB ← 节省 112 KB - 索引缓冲区: 4 bytes * 6 * 1000 24 KB - 总计: 88 KB 节省: 56% 内存2. 材质类型 (WORLD/UI) 和 renderpass 有什么关系?材质类型决定使用哪个着色器:材质类型 → 着色器 → Renderpass ┌──────────────────┐ │ MATERIAL_TYPE_ │ │ WORLD │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ Material Shader │ │ - Perspective │ │ - Lighting │ │ - Normal mapping │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ World Renderpass │ └──────────────────┘ ┌──────────────────┐ │ MATERIAL_TYPE_ │ │ UI │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ UI Shader │ │ - Orthographic │ │ - No lighting │ │ - Simple texture │ └────────┬─────────┘ │ ▼ ┌──────────────────┐ │ UI Renderpass │ └──────────────────┘系统根据材质类型自动选择着色器:// engine/src/renderer/vulkan/vulkan_backend.cb8vulkan_renderer_begin_renderpass(renderer_backend*backend,u8 renderpass_id){switch(renderpass_id){caseBUILTIN_RENDERPASS_WORLD:vulkan_material_shader_use(context,context.material_shader);break;caseBUILTIN_RENDERPASS_UI:vulkan_ui_shader_use(context,context.ui_shader);break;}returntrue;}// 绘制几何体时,根据材质类型应用不同的着色器状态voidvulkan_renderer_draw_geometry(geometry_render_data data){// 根据材质类型更新 shader 状态if(data.geometry-material-typeMATERIAL_TYPE_WORLD){vulkan_material_shader_apply_material(context,context.material_shader,data.geometry-material);}elseif(data.geometry-material-typeMATERIAL_TYPE_UI){vulkan_ui_shader_apply_material(context,context.ui_shader,data.geometry-material);}// 绘制vkCmdDrawIndexed(...);}错误示例 (材质类型与 renderpass 不匹配):// ❌ 错误:在 World Renderpass 中使用 UI 材质geometry_render_data data;data.geometryui_geometry;// material-type MATERIAL_TYPE_UIdata.modelmat4_identity();packet.geometry_count1;packet.geometriesdata;// ← 将 UI 几何体放到 World Renderpass// 结果:渲染错误,因为 Material Shader 期望 vertex_3d,但得到 vertex_2d正确使用:// ✓ 正确:UI 材质在 UI Renderpasspacket.ui_geometry_count1;packet.ui_geometriesui_data;// MATERIAL_TYPE_UI → UI Renderpass// ✓ 正确:World 材质在 World Renderpasspacket.geometry_count1;packet.geometriesworld_data;// MATERIAL_TYPE_WORLD → World Renderpass3. 为什么 UI 顶点使用屏幕像素坐标而不是归一化坐标?屏幕像素坐标更直观:// 方案 1:像素坐标 (Kohi 使用)vertex_2d ui_verts[4];ui_verts[0].position(vec2){0,0};// 左上角ui_verts[1].position(vec2){512,512};// 右下角// 优势:// - 直观:512 像素就是 512 像素// - 设计工具友好:设计师提供的尺寸可以直接使用// - 无需转换:100x100 的按钮就是 100x100 像素// 方案 2:归一化坐标 (0.0 ~ 1.0)vertex_2d ui_verts[4];ui_verts[0].position(vec2){0.0,0.0};ui_verts[1].position(vec2){0.4,0.7};// ← 不直观:这是多少像素?// 缺点:// - 不直观:需要心算转换// - 窗口大小相关:调整窗口大小需要重新计算// - 设计工具不友好:设计师需要转换单位坐标转换由正交投影矩阵完成:// 正交投影自动将像素坐标转换为 NDCmat4 ui_projectionmat4_orthographic(0,// left: 0 像素1280,// right: 1280 像素720,// bottom: 720 像素0,// top: 0 像素-100.0f,// near100.0f// far);// 顶点变换:// 像素坐标 (512, 512) → NDC (0.3, 0.7) → 屏幕坐标 (512, 512)布局示例:// 使用像素坐标布局 UI// 1. 按钮 (100x50),位于 (50, 50)vertex_2d button_verts[4];button_verts[0].position(vec2){0,0};button_verts[1].position(vec2){100,50};// model mat4_translation((vec3){50, 50, 0})// 最终位置:(50, 50) 到 (150, 100)// 2. 图标 (64x64),位于 (200, 100)vertex_2d icon_verts[4];icon_verts[0].position(vec2){0,0};icon_verts[1].position(vec2){64,64};// model mat4_translation((vec3){200, 100, 0})// 最终位置:(200, 100) 到 (264, 164)// 所见即所得:代码中的坐标 屏幕上的像素4. 泛型 create_geometry 接口会影响类型安全吗?是的,但可以通过包装函数缓解:// 原始泛型接口 (类型不安全)b8renderer_create_geometry(geometry*geometry,u32 vertex_size,u32 vertex_count,constvoid*vertices,// ← void* 可以接受任意类型u32 index_size,u32 index_count,constvoid*indices);// 问题:可能传入错误的 vertex_sizevertex_2d verts[4];renderer_create_geometry(geometry,sizeof(vertex_3d),// ← 错误!应该是 sizeof(vertex_2d)4,verts,sizeof(u32),6,indices);// 结果:GPU 读取错误的顶点数据,渲染混乱解决方案:提供类型安全的包装函数// 类型安全的包装函数b8renderer_create_geometry_3d(geometry*geometry,u32 vertex_count,constvertex_3d*vertices,u32 index_count,constu32*indices){returnrenderer_create_geometry(geometry,sizeof(vertex_3d),// ← 自动填充正确的大小vertex_count,vertices,sizeof(u32),index_count,indices);}b8renderer_create_geometry_2d(geometry*geometry,u32 vertex_count,constvertex_2d*vertices,u32 index_count,constu32*indices){returnrenderer_create_geometry(geometry,sizeof(vertex_2d),// ← 自动填充正确的大小vertex_count,vertices,sizeof(u32),index_count,indices);}// 使用包装函数 (类型安全)vertex_2d ui_verts[4];renderer_create_geometry_2d(geometry,4,ui_verts,6,indices);// 编译器会检查类型:ui_verts 必须是 vertex_2d*vertex_3d world_verts[100];renderer_create_geometry_3d(geometry,100,world_verts,300,indices);// 编译器会检查类型:world_verts 必须是 vertex_3d*或者使用 C 模板 (如果使用 C):templatetypenameVertexTypeboolrenderer_create_geometry_typed(geometry*geometry,u32 vertex_count,constVertexType*vertices,u32 index_count,constu32*indices){returnrenderer_create_geometry(geometry,sizeof(VertexType),// ← 自动推导大小vertex_count,vertices,sizeof(u32),index_count,indices);}// 使用 (完全类型安全)vertex_2d ui_verts[4];renderer_create_geometry_typed(geometry,4,ui_verts,6,indices);vertex_3d world_verts[100];renderer_create_geometry_typed(geometry,100,world_verts,300,indices);5. 如何调试 UI 几何体不显示的问题?常见原因和调试步骤:1. 检查材质类型:// 确保 UI 几何体使用 UI 材质KASSERT(ui_geometry-material-typeMATERIAL_TYPE_UI);// 检查材质文件// test_ui_material.kmt 必须包含: typeui2. 检查顶点格式:// 确保使用 vertex_2dgeometry_config ui_config;ui_config.vertex_sizesizeof(vertex_2d);// 必须是 16,不是 44KASSERT(ui_config.vertex_size16);3. 检查坐标范围:// UI 坐标必须在屏幕范围内// 假设屏幕大小 1280x720vertex_2d verts[4];verts[0].position(vec2){0,0};verts[1].position(vec2){512,512};// ← 必须 (1280, 720)// 检查模型矩阵mat4 modelmat4_translation((vec3){100,100,0});// 平移到可见区域// 最终位置:(100, 100) 到 (612, 612) ← 在屏幕内// 错误示例:mat4 modelmat4_translation((vec3){2000,2000,0});// ← 超出屏幕!4. 检查渲染顺序:// UI 必须在 UI Renderpass 中绘制render_packet packet;// 世界几何体 → World Renderpasspacket.geometry_count1;packet.geometriesworld_data;// UI 几何体 → UI Renderpasspacket.ui_geometry_count1;packet.ui_geometriesui_data;// ← 确保在 ui_geometries,不是 geometries5. 检查深度测试:// UI Renderpass 的深度测试可能导致问题// 确保 Z 坐标在正交投影范围内mat4 ui_projectionmat4_orthographic(0,1280,720,0,-100.0f,100.0f);// ^ ^// near farmat4 modelmat4_translation((vec3){100,100,0});// Z0 (在范围内)// 错误示例:mat4 modelmat4_translation((vec3){100,100,-200});// Z-200 (超出范围!)6. 使用 Vulkan 验证层:# 启用验证层查看错误exportVK_INSTANCE_LAYERSVK_LAYER_KHRONOS_validation# 检查输出:# - 顶点缓冲区绑定错误# - 顶点属性不匹配# - Descriptor set 未绑定7. 检查纹理:// 确保 UI 材质有有效的纹理texture*diffuse_texui_geometry-material-diffuse_map.texture;KASSERT(diffuse_tex!0);KASSERT(diffuse_tex-id!INVALID_ID);// 如果纹理加载失败,会回退到默认纹理 (白色或粉色)if(!diffuse_tex){KWARN(UI material has no texture, using default);diffuse_textexture_system_get_default_texture();} 练习练习 1: 创建多个 UI 元素任务:创建3个不同大小和位置的 UI 元素。// 1. 背景图片 (全屏)geometry_config bg_config;bg_config.vertex_sizesizeof(vertex_2d);bg_config.vertex_count4;bg_config.index_sizesizeof(u32);bg_config.index_count6;string_ncopy(bg_config.material_name,background_material,MATERIAL_NAME_MAX_LENGTH);vertex_2d bg_verts[4];bg_verts[0].position(vec2){0,0};bg_verts[1].position(vec2){1280,720};// 全屏// ... 设置其他顶点和纹理坐标 ...geometry*backgroundgeometry_system_acquire_from_config(bg_config,true);// 2. 按钮 (200x80)geometry_config button_config;// ... 类似设置 ...vertex_2d button_verts[4];button_verts[0].position(vec2){0,0};button_verts[1].position(vec2){200,80};// ...geometry*buttongeometry_system_acquire_from_config(button_config,true);// 3. 图标 (64x64)geometry_config icon_config;// ... 类似设置 ...vertex_2d icon_verts[4];icon_verts[0].position(vec2){0,0};icon_verts[1].position(vec2){64,64};// ...geometry*icongeometry_system_acquire_from_config(icon_config,true);// 4. 在渲染循环中绘制geometry_render_data ui_elements[3];// 背景 (Z0,最后面)ui_elements[0].geometrybackground;ui_elements[0].modelmat4_translation((vec3){0,0,0});// 按钮 (Z10)ui_elements[1].geometrybutton;ui_elements[1].modelmat4_translation((vec3){540,320,10});// 居中// 图标 (Z20,最前面)ui_elements[2].geometryicon;ui_elements[2].modelmat4_translation((vec3){50,50,20});// 左上角packet.ui_geometry_count3;packet.ui_geometriesui_elements;练习 2: 实现 UI 元素动画任务:实现一个简单的 UI 元素滑动动画。// 定义动画状态typedefstructui_animation{vec2 start_pos;vec2 end_pos;f32 duration;f32 elapsed;b8 active;}ui_animation;ui_animation button_anim;button_anim.start_pos(vec2){-200,320};// 屏幕左边外button_anim.end_pos(vec2){540,320};// 屏幕中央button_anim.duration1.0f;// 1 秒button_anim.elapsed0.0f;button_anim.activetrue;// 更新函数voidupdate_ui_animation(ui_animation*anim,f32 delta_time){if(!anim-active)return;anim-elapseddelta_time;if(anim-elapsedanim-duration){anim-elapsedanim-duration;anim-activefalse;}// 线性插值f32 tanim-elapsed/anim-duration;vec2 current_posvec2_lerp(anim-start_pos,anim-end_pos,t);// 更新模型矩阵button_render.modelmat4_translation((vec3){current_pos.x,current_pos.y,0});}// 在主循环中调用voidapplication_update(f32 delta_time){update_ui_animation(button_anim,delta_time);}进阶:缓动函数 (Easing)// 缓动函数:ease-in-outf32ease_in_out(f32 t){returnt0.5f?2.0f*t*t:-1.0f(4.0f-2.0f*t)*t;}// 使用缓动f32 tanim-elapsed/anim-duration;f32eased_tease_in_out(t);vec2 current_posvec2_lerp(anim-start_pos,anim-end_pos,eased_t);练习 3: 实现 9-Slice UI任务:实现9-slice (九宫格) UI 元素,可以任意缩放而不失真。/** * 9-Slice 布局: * ┌───┬───────┬───┐ * │ 0 │ 1 │ 2 │ ← 顶部 (不拉伸 Y) * ├───┼───────┼───┤ * │ 3 │ 4 │ 5 │ ← 中间 (拉伸 X 和 Y) * ├───┼───────┼───┤ * │ 6 │ 7 │ 8 │ ← 底部 (不拉伸 Y) * └───┴───────┴───┘ * ↑ ↑ ↑ * 不拉伸 拉伸 不拉伸 */typedefstructnine_slice_config{f32 width;// 目标宽度f32 height;// 目标高度f32 border_left;// 左边框大小f32 border_right;// 右边框大小f32 border_top;// 上边框大小f32 border_bottom;// 下边框大小constchar*texture_name;}nine_slice_config;geometry*create_nine_slice_ui(nine_slice_config config){// 创建9个 quad,每个 quad 对应一个区域// 区域 0:左上角 (固定大小)vertex_2d verts_0[4];verts_0[0].position(vec2){0,0};verts_0[1].position(vec2){config.border_left,config.border_top};verts_0[0].texcoord(vec2){0,0};verts_0[1].texcoord(vec2){0.33f,0.33f};// 纹理前 1/3// 区域 1:顶部中间 (拉伸 X)vertex_2d verts_1[4];verts_1[0].position(vec2){config.border_left,0};verts_1[1].position(vec2){config.width-config.border_right,config.border_top};verts_1[0].texcoord(vec2){0.33f,0};verts_1[1].texcoord(vec2){0.67f,0.33f};// 纹理中间 1/3// ... 类似创建区域 2-8 ...// 区域 4:中心 (拉伸 X 和 Y)vertex_2d verts_4[4];verts_4[0].position(vec2){config.border_left,config.border_top};verts_4[1].position(vec2){config.width-config.border_right,config.height-config.border_bottom};verts_4[0].texcoord(vec2){0.33f,0.33f};verts_4[1].texcoord(vec2){0.67f,0.67f};// 合并所有顶点和索引// ...// 创建几何体geometry_config geo_config;geo_config.vertex_sizesizeof(vertex_2d);geo_config.vertex_count36;// 9 quad * 4 verticesgeo_config.index_count54;// 9 quad * 6 indices// ...returngeometry_system_acquire_from_config(geo_config,true);}// 使用nine_slice_config panel_config;panel_config.width400;panel_config.height300;panel_config.border_left16;panel_config.border_right16;panel_config.border_top16;panel_config.border_bottom16;panel_config.texture_namepanel_border;geometry*panelcreate_nine_slice_ui(panel_config);恭喜!你已经掌握了在 UI Renderpass 中绘制!关注公众号「上手实验室」,获取更多游戏引擎开发教程!Tutorial written by 上手实验室
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

邯郸网站制作官网手机优化不足80怎么办

该栏目仅列出了部分常用的应用集成使用教程,并非只有这几个应用才能使用。 我们的API已经完全适配OpenAI格式,市面上任何兼用OpenAI的应用或开发工具都可以调用。如果您在使用其他工具,但不知道如何配置,可以联系客服协助配置。 在…

张小明 2025/12/24 9:44:58 网站建设

网站怎么做备案号超链接安阳做一个网站多少钱

C++ 异常处理:从 set jump 到 C++ 异常 1. set jump 异常 set jump 异常可视为 C 风格的异常。与 C++ 风格的异常类似,它能让用户在代码中设置一个出错时返回的位置,还提供了生成执行跳转异常的方法。 以下是一个示例代码: #include <cstring> #include <cse…

张小明 2025/12/24 9:51:00 网站建设

北京P2P公司网站建设wordpress mysql 扩展

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

张小明 2025/12/22 6:35:02 网站建设

找什么人做公司网站室内设计图片大全

Kotaemon在电商客服中的落地实践分享 在电商平台的日常运营中&#xff0c;一个常见的场景是&#xff1a;凌晨两点&#xff0c;一位用户焦急地发来消息&#xff1a;“我昨天下单的手机还没发货&#xff0c;是不是出问题了&#xff1f;” 如果依赖人工客服&#xff0c;这条消息可…

张小明 2026/1/1 13:13:53 网站建设

网站备案账号是什么样的做同款的网站

OpenArm&#xff1a;重新定义开源机械臂的人机协作新时代 【免费下载链接】OpenArm OpenArm v0.1 项目地址: https://gitcode.com/gh_mirrors/op/OpenArm 欢迎来到OpenArm的开源机械臂世界&#xff01;作为一款专为现代机器人研究打造的创新平台&#xff0c;OpenArm通过…

张小明 2025/12/29 11:55:29 网站建设