DX11中如何设置着色器实现部分物体纯色部分纹理渲染?
实现同一场景中纯色与纹理物体共存的渲染方案(Windows 10 SDK)
我之前用Windows 10 SDK做3D渲染的时候也碰到过这个问题——网上教程大多只讲单一渲染模式,很少提混合场景的实现。其实不用纠结只能二选一,有两种靠谱的方案可以实现,我给你拆解清楚:
方案一:通用着色器(推荐,减少状态切换开销)
这种方案是把颜色和纹理坐标都整合到着色器结构体里,通过一个控制标记来动态选择渲染模式,不用频繁切换着色器。
1. 着色器结构体定义
把两种顶点属性都包含进去,这样不管是纯色还是纹理物体,都能共用一套输入输出:
// 顶点输入结构体:包含位置、顶点颜色、纹理坐标 struct VertexIn { float4 Pos : POSITION; float4 Color : COLOR; float2 Tex : TEXCOORD0; }; // 顶点输出结构体:传递裁剪空间位置、顶点颜色、纹理坐标到像素着色器 struct VertexOut { float4 Pos : SV_POSITION; float4 Color : COLOR; float2 Tex : TEXCOORD0; };
2. 顶点着色器实现
顶点着色器的工作就是完成坐标变换,同时把颜色和纹理坐标原封不动传递给像素着色器:
// 常量缓冲区:存放世界/视图/投影矩阵 cbuffer MatrixBuffer : register(b0) { float4x4 World; float4x4 View; float4x4 Projection; }; VertexOut VS(VertexIn input) { VertexOut output; // 坐标空间转换:本地空间 → 世界空间 → 视图空间 → 裁剪空间 float4 posWorld = mul(input.Pos, World); float4 posView = mul(posWorld, View); output.Pos = mul(posView, Projection); // 直接传递顶点颜色和纹理坐标 output.Color = input.Color; output.Tex = input.Tex; return output; }
3. 像素着色器实现
通过一个常量缓冲区的标记来决定是用纯色还是采样纹理,同时支持纹理和顶点颜色的混合(比如给纹理加 tint 效果):
// 渲染控制常量缓冲区:标记当前物体的渲染模式 cbuffer RenderControlBuffer : register(b1) { int UseTexture; // 0 = 纯色渲染,1 = 纹理渲染 }; // 纹理和采样器资源 Texture2D ObjTexture : register(t0); SamplerState ObjSampler : register(s0); float4 PS(VertexOut input) : SV_TARGET { if (UseTexture == 1) { // 采样纹理,可选和顶点颜色相乘实现颜色叠加 float4 texColor = ObjTexture.Sample(ObjSampler, input.Tex); return texColor * input.Color; } else { // 直接返回顶点颜色,实现纯色渲染 return input.Color; } }
4. 渲染流程注意点
- 渲染纯色物体时:
- 顶点数据里的纹理坐标可以随便填(比如
float2(0,0)),不会影响结果 - 更新
RenderControlBuffer里的UseTexture为0 - 不需要绑定纹理/采样器(绑定了也没关系,因为逻辑会跳过采样)
- 顶点数据里的纹理坐标可以随便填(比如
- 渲染纹理物体时:
- 正确填充顶点的纹理坐标
- 更新
RenderControlBuffer里的UseTexture为1 - 绑定对应的纹理和采样器资源
方案二:两套独立着色器(适合简单场景)
如果场景里物体类型区分很明确,也可以直接用你给出的两套结构体,分别写对应的着色器,渲染时切换着色器即可:
纯色渲染着色器
struct VertexIn { float4 Pos : POSITION; float4 Color : COLOR; }; struct VertexOut { float4 Pos : SV_POSITION; float4 Color : COLOR; }; cbuffer MatrixBuffer : register(b0) { float4x4 World; float4x4 View; float4x4 Projection; }; VertexOut VS(VertexIn input) { VertexOut output; float4 posWorld = mul(input.Pos, World); float4 posView = mul(posWorld, View); output.Pos = mul(posView, Projection); output.Color = input.Color; return output; } float4 PS(VertexOut input) : SV_TARGET { return input.Color; }
纹理渲染着色器
struct VS_INPUT { float4 Pos : POSITION; float2 Tex : TEXCOORD0; }; struct PS_INPUT { float4 Pos : SV_POSITION; float2 Tex : TEXCOORD0; }; cbuffer MatrixBuffer : register(b0) { float4x4 World; float4x4 View; float4x4 Projection; }; Texture2D ObjTexture : register(t0); SamplerState ObjSampler : register(s0); PS_INPUT VS(VS_INPUT input) { PS_INPUT output; float4 posWorld = mul(input.Pos, World); float4 posView = mul(posWorld, View); output.Pos = mul(posView, Projection); output.Tex = input.Tex; return output; } float4 PS(PS_INPUT input) : SV_TARGET { return ObjTexture.Sample(ObjSampler, input.Tex); }
渲染流程注意点
- 渲染纯色物体时,设置并使用纯色渲染的着色器
- 渲染纹理物体时,切换到纹理渲染的着色器,同时绑定对应的纹理和采样器
两种方案里,通用着色器的方式更高效,因为减少了着色器切换的状态开销,适合复杂场景;两套着色器更直观,适合简单场景,你可以根据自己的项目需求选择。
内容的提问来源于stack exchange,提问作者spartawhy117
相关产品推荐
相关产品推荐

