公司網(wǎng)站建設(shè)方案詳細(xì)方案軟文推廣產(chǎn)品
games106 homework1
gltf介紹圖:
骨骼動(dòng)畫
動(dòng)畫相關(guān)屬性:
對(duì)GLTF的理解參照了這篇文章:
glTF格式詳解(動(dòng)畫)
GLTF文件格式詳解
buffer和bufferView對(duì)象用于引用動(dòng)畫數(shù)據(jù)。 buffer對(duì)象用來指定原始動(dòng)畫數(shù)據(jù), bufferView對(duì)象用來引用buffer對(duì)象。比如下面的bufferView對(duì)象引用了索引為0的指定了原始動(dòng)畫數(shù)據(jù)的buffer對(duì)象。
"buffers": [{"byteLength": 2514732,"uri": "busterDrone.bin"}
],
"bufferViews": [{"buffer": 0,"byteLength": 196356,"byteOffset": 0,"byteStride": 0,"target": 34963},{"buffer": 0,"byteLength": 99000,"byteOffset": 196356,"byteStride": 0,"target": 34962},
...
]
Accessor對(duì)象用于描述原始動(dòng)畫數(shù)據(jù)的結(jié)構(gòu)。 count屬性表示動(dòng)畫數(shù)據(jù)包含了1190個(gè)動(dòng)畫關(guān)鍵幀的計(jì)時(shí)信息,每個(gè)計(jì)時(shí)信息是一個(gè)float類型的標(biāo)量,所有計(jì)時(shí)信息占用了14280個(gè)字節(jié)。第二個(gè)accessor對(duì)象引用的數(shù)據(jù)在計(jì)時(shí)信息這14280字節(jié)之后,共有1190個(gè)元素,每個(gè)元素是一個(gè)包含3個(gè)分量類型為float的向量,推測SCALAR類型為計(jì)時(shí)信息,VEC3為平移信息,VEC4為旋轉(zhuǎn)四元數(shù)。
"accessors": [{"bufferView": 3,"byteOffset": 0,"componentType": 5126,"count": 1190,"max": [ 0.06134279, 0.07975265, 0.02774119 ],"min": [ -0.06144484, -0.1283657, -0.09091433 ],"type": "VEC3"},{"bufferView": 3,"byteOffset": 14280,"componentType": 5126,"count": 1190,"max": [ 1, 1, 1 ],"min": [ -1, -1, -1 ],"type": "VEC3"},{"bufferView": 2,"byteOffset": 0,"componentType": 5126,"count": 1190,"max": [ 0.9694519, 0.997895 ],"min": [ 0.001107991, 0.001113892 ],"type": "VEC2"},
...
]
Animation包含samplers和channels。samplers數(shù)組對(duì)象,用于描述動(dòng)畫數(shù)據(jù)來源。samplers數(shù)組對(duì)象,用于描述動(dòng)畫數(shù)據(jù)來源。
sampler對(duì)象包含了input和output屬性,這兩個(gè)屬性通過索引來引用accessor對(duì)象 。這里的input屬性引用了索引為2的用于計(jì)時(shí)信息accessor對(duì)象,output屬性引用了索引 為3的用于旋轉(zhuǎn)信息的accessor對(duì)象。此外,sampler對(duì)象還有包含了一個(gè)interpolation屬性,用于指定插值方式,這里的示例使用的LINEAR插值方式。
channel對(duì)象用于在動(dòng)畫數(shù)據(jù)和node對(duì)象之間建立聯(lián)系 ,指定動(dòng)畫所作用的node對(duì)象。
"samplers": [{"input": 60,"interpolation": "LINEAR","output": 61},
...
]"channels": [{"sampler": 0,"target": {"node": 8,"path": "translation"}},
...
]
代碼修改:
詳細(xì)查看了下gltfskinning.cpp中的動(dòng)畫代碼。這個(gè)example讀取的gltf文件多出來了一個(gè)skins屬性,skins中包含inverseBindMatrices,joints,skeleton屬性。
joints記錄了作為關(guān)節(jié)點(diǎn)的node索引。gltf中的skeleton的形式更為簡單,它包含著根骨骼的Node索引。inverseBindMatrices是gltf幫忙計(jì)算好的模型空間變換到對(duì)應(yīng)骨骼空間的矩陣。
loadSkin函數(shù)把相關(guān)的joints node 存儲(chǔ)在skin.joints 的容器中。我自己理解的意思是在render時(shí),從骨骼的根節(jié)點(diǎn)開始依次處理joints及節(jié)點(diǎn)的變換。
對(duì)于結(jié)構(gòu)體的更新可以直接參考gltfskinning.cpp去更新Node,添加AnimationSampler,AnimationChannel,Animation結(jié)構(gòu)體。
對(duì)于動(dòng)畫的加載loadAnimation可以直接copy gltfskinning.cpp中的代碼,更新時(shí)update Animation思路也相似,然而作業(yè)中的gltf文件沒有蒙皮skins屬性,所以不能直接使用updateJoint函數(shù)更新,此處動(dòng)畫的更新需要逐個(gè)去更新節(jié)點(diǎn)的位置。
初始代碼的drawNode函數(shù)中表明最終繪制節(jié)點(diǎn)時(shí)用到的位置為node.matrix,通過vkCmdPushConstants傳入shader對(duì)應(yīng)的參數(shù)就是primitive. model。
// drawNode Funcglm::mat4 nodeMatrix = node.matrix;VulkanglTFModel::Node *currentParent = node.parent;while (currentParent){nodeMatrix = currentParent->matrix * nodeMatrix;currentParent = currentParent->parent;}// Pass the final matrix to the vertex shader using push constantsvkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix);
上面這一段drawNode函數(shù)的代碼很重要,它實(shí)現(xiàn)的是一個(gè)全靜態(tài)模型的傳值。currentParent->matrix是初始loadNode時(shí)根據(jù)translation,rotate,scale計(jì)算得到的值,這個(gè)值在后面是沒有再被更新的。
在update Animation函數(shù)中,根據(jù)動(dòng)畫的數(shù)據(jù)對(duì)每個(gè)時(shí)間node的translation,rotate,scale的值進(jìn)行了插值計(jì)算更新,而傳值得到的matrix并沒有得到更新。
在drawNode中應(yīng)該使用 getLocalMatrix()去計(jì)算此刻的matrix,同時(shí)不能漏掉node的parent更新,否則繪制出模型的就會(huì)錯(cuò)位(我就是這么做的,檢查了好久的問題QAQ)。
glm::mat4 getLocalMatrix()
{return glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) * matrix;
}// ** update nodeMatrix **
glm::mat4 nodeMatrix = node->getLocalMatrix(); //node->matrix;
VulkanglTFModel::Node* currentParent = node->parent;
while (currentParent) {nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;currentParent = currentParent->parent;
}
在初始化loadNode時(shí),不需要更改node->matrix的值,加載初始的translation,rotate,scale即可。
// Get the local node matrix
// It's either made up from translation, rotation, scale or a 4x4 matrix
if (inputNode.translation.size() == 3) {//node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));node->translation = glm::make_vec3(inputNode.translation.data());
}
if (inputNode.rotation.size() == 4) {glm::quat q = glm::make_quat(inputNode.rotation.data());//node->matrix *= glm::mat4(q);node->rotation = q;
}
if (inputNode.scale.size() == 3) {//node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));node->scale = glm::make_vec3(inputNode.scale.data());
}
if (inputNode.matrix.size() == 16) {node->matrix = glm::make_mat4x4(inputNode.matrix.data());
};
由于計(jì)算以后沒有push constant使得shader的參數(shù)更新,所以動(dòng)畫并不會(huì)顯示出來。在render()中updateAnimation后重新調(diào)用一遍buildCommandBuffer即可。
PBR材質(zhì)
PBR材質(zhì)介紹:
PBR材質(zhì)相關(guān)理解記錄在了這篇文章里,作業(yè)中的實(shí)現(xiàn)參考了pbrbasic, pbribl, pbrtexture中的實(shí)現(xiàn)。
PBR材質(zhì)講解
數(shù)據(jù)結(jié)構(gòu)更新
pbr.cpp文件中pushconsts中傳遞了一些屬性,然而在作業(yè)中 roughness,metallic,包括rgb參數(shù)其實(shí)都不需要cpp傳遞,可以直接從紋理圖片中獲取。所以直接更新Material的struct為下,同步修改loadMaterial中對(duì)材質(zhì)參數(shù)的加載。
struct Material {uint32_t baseColorTextureIndex;uint32_t metallicRoughnessTextureIndex;uint32_t normalTextureIndex = -1;uint32_t emissiveTextureIndex = -1;uint32_t occlusionTextureIndex = -1;VkDescriptorSet descriptorSet;};
如下是mesh的相關(guān)屬性,primitives對(duì)應(yīng)的是vertex基礎(chǔ)屬性,material是gltf文件中materials中材質(zhì)的索引。gltf相關(guān)圖片中已經(jīng)寫的比較清楚,就不再細(xì)述。
"meshes": [
{"name": "mesh_L_P4_17366L_P4","primitives": [{"attributes": {"POSITION": 0,"NORMAL": 1,"TEXCOORD_0": 2,"TANGENT": 3},"indices": 4,"material": 0,"mode": 4}]
},
]
根據(jù)primitives中的屬性,應(yīng)該給Vertex 新加上一個(gè)tangent屬性。
在loadNode的過程中,會(huì)根據(jù)mesh的primitives數(shù)據(jù)向vertexBuffer中添加數(shù)據(jù),Vertex新增屬性tangent后也需要更新loadNode代碼。
struct Vertex {glm::vec3 pos;glm::vec3 normal;glm::vec2 uv;glm::vec3 color;glm::vec4 tangent;};
在preparePipeline()中,需要更新VkVertexInputAttributeDescription的屬性。
const std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(VulkanglTFModel::Vertex, pos)), // Location 0: Positionvks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(VulkanglTFModel::Vertex, normal)),// Location 1: Normalvks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, offsetof(VulkanglTFModel::Vertex, uv)), // Location 2: Texture coordinatesvks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32_SFLOAT, offsetof(VulkanglTFModel::Vertex, color)), // Location 3: Colorvks::initializers::vertexInputAttributeDescription(0, 4, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(VulkanglTFModel::Vertex, tangent)), // Location 4: tangent
};
DescriptorBinding更新
數(shù)據(jù)結(jié)構(gòu)和pipeline相關(guān)進(jìn)行更新后也需要更新Descriptor的綁定。
在setupDescriptors()中對(duì)poolSizes,descriptorPoolInfo進(jìn)行修改。VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER對(duì)應(yīng)的是shader中的ubo,數(shù)量只有一個(gè),因?yàn)閙aterial沒有多余屬性需要其他的ubo傳遞。VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER顧名思義是綁定的圖片采樣器,每張紋理有5個(gè)texture index。
std::vector<VkDescriptorPoolSize> poolSizes = { // matrices + materials uniform buffervks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1),// One combined image sampler per model image/texturevks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, static_cast<uint32_t>(5 * glTFModel.materials.size())),
};
對(duì)于這兩個(gè)類型的descriptior需要分別設(shè)定它們的layout。
對(duì)于uniform設(shè)定為VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,紋理設(shè)定為VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER。
// Descriptor set layout for passing matrices
VkDescriptorSetLayoutBinding setLayoutBinding = vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0);
// Descriptor set layout for passing material textures
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0),vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1),vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 2),vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 3),vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 4)
};
根據(jù)descriptorPool和descriptorLayout的相關(guān)信息得到VkDescriptorSetAllocateInfo,并根據(jù)此信息調(diào)用vkAllocateDescriptorSets分配descriptor。最后再用vks::initializers::writeDescriptorSet把每個(gè)material對(duì)應(yīng)的5張紋理的descriptor寫入到material的descriptor中。
// Descriptor sets for materials textures
for (auto& mat : glTFModel.materials) {const VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.textures, 1);VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &mat.descriptorSet));std::vector<VkWriteDescriptorSet> writeDescriptorSets = {vks::initializers::writeDescriptorSet(mat.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &glTFModel.images[mat.baseColorTextureIndex].texture.descriptor),vks::initializers::writeDescriptorSet(mat.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &glTFModel.images[mat.metallicRoughnessTextureIndex].texture.descriptor),vks::initializers::writeDescriptorSet(mat.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, &glTFModel.images[mat.normalTextureIndex].texture.descriptor),};if (mat.emissiveTextureIndex >= 0 && mat.emissiveTextureIndex < glTFModel.images.size()) writeDescriptorSets.push_back(vks::initializers::writeDescriptorSet(mat.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3, &glTFModel.images[mat.emissiveTextureIndex].texture.descriptor));if (mat.occlusionTextureIndex >= 0 && mat.occlusionTextureIndex < glTFModel.images.size()) writeDescriptorSets.push_back(vks::initializers::writeDescriptorSet(mat.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4, &glTFModel.images[mat.occlusionTextureIndex].texture.descriptor));vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
}
如果material有其他一些參數(shù)要傳入的話,可以考慮新建一個(gè)uniform buffer,此時(shí)需要對(duì)descriptorPoolSize的大小進(jìn)行更新,對(duì)新建的uniform buffer的descriptor進(jìn)行初始化,
除此外還需要參照Vertex shader uniform buffer block的建立,在prepareUniformBuffers()函數(shù)中給每個(gè)material新建一個(gè)uniform buffer并map。
著色器編譯:
參照以下:
HLSL in Vulkan :: Vulkan Documentation Project Demo
vulkan讀取的是編譯后的spv文件,對(duì)于.frag和.vert文件編譯??梢哉业絭ulkansdk/bin/Win32/glslangValidator.exe的位置對(duì)glsl文件進(jìn)行編譯。
# glsl 編譯
glslangValidator mesh.frag -V100 -o mesh.frag.spv
glslangValidator mesh.vert -V100 -o mesh.vert.spv# hlsl編譯
dxc -spirv -T vs_6_0 -E main .\mesh.vert -Fo .\mesh.vert.spv
dxc -spirv -T ps_6_0 -E main .\mesh.frag -Fo .\mesh.frag.spv
mark一下: hlsl中的texture register,space
關(guān)鍵字 (keyword) 指定聲明變量綁定到的邏輯寄存器空間。該關(guān)鍵字省略默認(rèn)是space0,register(t3, space0)
永遠(yuǎn)不會(huì)與 register(t3, space1)
沖突,也永遠(yuǎn)不會(huì)與另一個(gè)空間中可能包含 t3 的任何數(shù)組沖突。
要注意space的分配,分配不當(dāng)程序運(yùn)行時(shí)可能會(huì)出現(xiàn)一些問題。
Texture2D<float4> tex1 : register(t3, space0)
Texture2D<float4> tex2[4] : register(t10)
Texture2D<float4> tex3[7][5][3] : register(t20, space1)
ToneMapping Pass
這部分的實(shí)現(xiàn)參考了,嘗試了離屏渲染方法:
GAMES106 作業(yè)1 問題整理(Tone Mapping 部分)
上面講述了兩個(gè)方案,render pass和subpass。
render pass思路概括:將原先繪制模型的pass繪制結(jié)果作為圖片存儲(chǔ)在對(duì)應(yīng)的frame buffer里,進(jìn)行后處理后再渲染到屏幕上。為此需要?jiǎng)?chuàng)建新的render pass和frame buffer對(duì)原來的模型繪制進(jìn)行離屏渲染。
用新增的一個(gè)后處理pass進(jìn)行tonemapping,需要新增一個(gè)實(shí)現(xiàn)tonemapping的shader,然后添加一個(gè)新的pipeline去加載這個(gè)新的shader module。需要新增實(shí)現(xiàn)的部分就是下圖中從pipeline向上到DescriptorSetLayout部分。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-8UaHyIIp-1691162057283)(/images/games106homewrok1/Untitled%201.png)]
離屏渲染RenderPass
離屏渲染的案例可以參考vulkan示例的bloom。
renderDoc調(diào)試觀察bloom.exe, 這個(gè)程序被劃分為了3個(gè)pass。colorpass(offscreen)→blur→scene。先對(duì)colorpass進(jìn)行離屏渲染,然后將渲染結(jié)果blur模糊處理,最后在場景繪制中將離屏渲染的內(nèi)容也繪制上。
對(duì)相關(guān)關(guān)系不是很熟練,所以如下圖所示整理了一下bloom這個(gè)程序中的相關(guān)綁定,便于自己理解相關(guān)綁定。
-
資源綁定
首先需要對(duì)原先的數(shù)據(jù)結(jié)構(gòu)進(jìn)行修改。修改pipelines結(jié)構(gòu)體,新增VkPipeline tonemap。
struct Pipelines {VkPipeline solid;VkPipeline wireframe = VK_NULL_HANDLE;VkPipeline tonemap = VK_NULL_HANDLE; } pipelines;
對(duì)于新增的VkPipeline tonemap,也要?jiǎng)?chuàng)建一個(gè)VkPipelineLayout和VkDescriptorSet與之對(duì)應(yīng)。同步更新對(duì)應(yīng)的DescriptorSetLayouts結(jié)構(gòu)體.
struct {VkPipelineLayout pipelineLayout;VkPipelineLayout postPipelineLayout; } pipelineLayouts;struct {VkDescriptorSet descriptorSet;VkDescriptorSet postDescriptorSet; } descriptorSets;struct DescriptorSetLayouts {VkDescriptorSetLayout matrices;VkDescriptorSetLayout textures;VkDescriptorSetLayout post; } descriptorSetLayouts;
在setupDescriptors(),需要設(shè)置后處理pass的descriptor layout,把后處理pass需要用到的資源綁定到流水線中。
對(duì)于新的tonemap pass,只需要將一張offscreen渲染得到的圖片傳給shader即可,不再需要uniform input。
mark:記得修改descriptor poolSizes。
在preparaPipeline()中,需要?jiǎng)?chuàng)建VkGraphicsPipelineCreateInfo去記錄pipeline基本的信息。其中pipelineCI.pVertexInputState綁定了vertexInputStateCI,即vertex輸入信息,它會(huì)和vertexInputAttributes聯(lián)系起來,記錄要傳遞到shader的輸入信息。
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayouts.pipelineLayout, renderPass, 0);pipelineCI.pVertexInputState = &vertexInputStateCI;pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;pipelineCI.pRasterizationState = &rasterizationStateCI;pipelineCI.pColorBlendState = &colorBlendStateCI;pipelineCI.pMultisampleState = &multisampleStateCI;pipelineCI.pViewportState = &viewportStateCI;pipelineCI.pDepthStencilState = &depthStencilStateCI;pipelineCI.pDynamicState = &dynamicStateCI;pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());pipelineCI.pStages = shaderStages.data();
而tonemap pass不需要繪制模型,所以不需要額外的vertex輸入,所以pipelineCI.pVertexInputState傳入為emptyInputState。
此外,表面剔除應(yīng)該被禁用。pipelineCI.pRasterizationState中的cull mode需要修改為VK_CULL_MODE_NONE。
綁定好要用到的資源后,在buildCommandBuffers()中將繪制模型的pass中的renderPass和framebuffer改為offscreenPass的renderPass和framebuffer,此時(shí)模型被繪制在offscreenPass的frameBuffer中,運(yùn)行將不再顯示。
renderPassBeginInfo.renderPass = offscreenPass.renderPass; renderPassBeginInfo.framebuffer = offscreenPass.frameBuffer;
之后在buildCommandBuffers()中新增tonemap pass,可以直接參考bloom中的代碼。令其framebuffer = frameBuffers[i]。renderPass = renderPass;。最后的繪制命令利用vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0)即可。
-
shader實(shí)現(xiàn)
tonemap pass的 frag shader已經(jīng)給出,vert shader可以直接參照bloom中的gaussblur實(shí)現(xiàn)。
下面記錄一些我遇到的花時(shí)間解決的問題。
我的shader是hlsl實(shí)現(xiàn),做出來是這個(gè)樣子,百思不得其解…不清楚是怎么把第一個(gè)pass生成的而圖像傳入第二個(gè)pass輸入中的。調(diào)試分析了下應(yīng)該是tonemap pass的 frag shader中的Texture2D textureColor讀取了繪制模型最后一個(gè)material的紋理。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-M2o0Alec-1691162057285)(/images/games106homewrok1/Untitled%205.png)]
后續(xù)我將frag shader中space1修改為了space0,就能正確的繪制了。應(yīng)該是原先的資源都被綁定到了邏輯寄存空間space1中,而新增的資源則是被綁定到默認(rèn)的space0中。相關(guān)比較好的解釋參考下面的鏈接:
D3D12 RootSignature
Texture2D textureColor : register(t0, space0); SamplerState samplerColor : register(s0, space0);
下面是tonemap前后效果的對(duì)比截圖。