本人使用的版本是ogre-next2.3
一、OgreSTBICodec.cpp 中decode()函数 解码24位图片有bug,会导致jpg或者24位png无法显示
修复如下:
Codec::DecodeResult STBIImageCodec::decode(DataStreamPtr& input) const
{
// Buffer stream into memory (TODO: override IO functions instead?)
MemoryDataStream memStream(input, true);
int width, height, components;
stbi_uc *pixelData = stbi_load_from_memory(
memStream.getPtr(), static_cast( memStream.size() ), &width, &height, &components, 0 );
if (!pixelData)
{
OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
"Error decoding image: " + String(stbi_failure_reason()),
"STBIImageCodec::decode");
}
ImageData2* imgData = OGRE_NEW ImageData2();
imgData->box.depth = 1u; // only 2D formats handled by this codec
imgData->box.numSlices = 1u;
imgData->box.width = static_cast( width );
imgData->box.height = static_cast( height );
imgData->numMipmaps = 1u; // no mipmaps in non-DDS
imgData->textureType = TextureTypes::Type2D;
switch( components )
{
case 1:
imgData->format = PFG_R8_UNORM;
break;
case 2:
imgData->format = PFG_RG8_UNORM;
break;
case 3:
imgData->format = PFG_RGBA8_UNORM;
break;
case 4:
imgData->format = PFG_RGBA8_UNORM;
break;
default:
stbi_image_free(pixelData);
OGRE_DELETE imgData;
OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
"Unknown or unsupported image format",
"STBIImageCodec::decode");
break;
}
const uint32 rowAlignment = 4u;
imgData->box.bytesPerPixel = PixelFormatGpuUtils::getBytesPerPixel( imgData->format );
imgData->box.bytesPerRow = PixelFormatGpuUtils::getSizeBytes( imgData->box.width,
1u, 1u, 1u,
imgData->format,
rowAlignment );
imgData->box.bytesPerImage = imgData->box.bytesPerRow * imgData->box.height;
imgData->box.data = OGRE_MALLOC_SIMD( imgData->box.bytesPerImage, MEMCATEGORY_RESOURCE );
if( components != 3 )
memcpy( imgData->box.data, pixelData, imgData->box.bytesPerImage );
else
{
size_t realBytesPerRow = PixelFormatGpuUtils::getSizeBytes( imgData->box.width, 1u, 1u, 1u,
PFG_RGB8_UNORM, rowAlignment );
for( size_t y = 0; y < (size_t)height; ++y )
{
uint8 *pDst = reinterpret_cast( imgData->box.at( 0u, y, 0u ) );
uint8 const *pSrc = pixelData + y * realBytesPerRow;
for( size_t x = 0; x < (size_t)width; ++x )
{
const uint8 b = *pSrc++;
const uint8 g = *pSrc++;
const uint8 r = *pSrc++;
*pDst++ = b;
*pDst++ = g;
*pDst++ = r;
*pDst++ = 0xFF;
}
}
}
stbi_image_free(pixelData);
DecodeResult ret;
ret.first.reset();
ret.second = CodecDataPtr(imgData);
return ret;
}
二、PBR材质使用
一个简单的材质如下
hlms FireHydrant_01_Green_FireHydrant_01_Green pbs
{
roughness 0.38
fresnel 1.33
specular 1.0 1.0 1.0
diffuse 3.14 3.14 3.14
emissive 0.05 0.05 0.05
workflow metallic_workflow
diffuse_map FireHydrant_01_Green_C.png
normal_map FireHydrant_01_N.png
//roughness_map FireHydrant_01_R.png
specular_map FireHydrant_01_M.png
}
注意Ogre支持三种PBR的工作流定义在OgreHlmsPbsDatablock.h文件中。默认的工作流是
SpecularWorkflow这个和国内常用的MetallicWorkflow不一致。
enum Workflows
{
/// Specular workflow. Many popular PBRs use SpecularAsFresnelWorkflow
/// though. @see setWorkflow
SpecularWorkflow,
/// Specular workflow where the specular texture is addressed to the fresnel
/// instead of kS. This is normally referred as simply Specular workflow
/// in many other PBRs. @see setWorkflow
SpecularAsFresnelWorkflow,
Metallic workflow. @see setWorkflow
MetallicWorkflow,
};
修改了OgreHlmsPbsDatablock.cpp文件中HlmsPbsDatablock的构造函数,增加Pbr材质中
workflow 关键字的支持,以通过材质指定工作流。增加代码如下:
if( Hlms::findParamInVec( params, "uv_detail_weight_map", paramVal ) )
{
setTextureUvSource( PBSM_DETAIL_WEIGHT,
StringConverter::parseUnsignedInt( paramVal ) );
}
if( Hlms::findParamInVec( params, "workflow", paramVal ) )
{
if(paramVal.compare("metallic_workflow") == 0 )
{
setWorkflow( MetallicWorkflow );
}
else if (paramVal.compare("fresnel_workflow") == 0 )
{
setWorkflow( SpecularAsFresnelWorkflow );
}
else
{
setWorkflow( SpecularWorkflow );
}
}
另外如果我们想知道pbr材质中支持哪些参数也可以查看该函数。比如:
“background_diffuse” “diffuse” “specular” “diffuse_map” 等等
三、调试Pbs生成的shader. HLMS材质系统通过各种预定义的语法片段来生成最终shader.
例如在prb材质中设置好
specular_map FireHydrant_01_M.png
则会用到 “Hlms/Pbs/Any/Main/800.PixelShader_piece_ps.any” 这个文件中的片段来生成对specular_map 采样的shader。我们可以通过修改这个文件来调试这部分代码例如修改该文件
如下。增加一个中间变量 fff 合 ddd 可以看到在最中的shader中会存在该部分。在debug模式下
ogrenext会默认将shader保存成文件。ogre保存该调试shader代码的函数在 OgreHlms.cpp文件中的 void Hlms::compileShaderCode( ShaderCodeCache &codeCache )函数中的
if( mDebugOutput )
debugDumpFile.write( &outString[0], outString.size() );
@piece( SampleSpecularMap )
/// SPECUlAR MAP
pixelData.specular.xyz = material.kS.xyz;
@property( !metallic_workflow )
pixelData.F0 = material.F0.@insertpiece( FresnelSwizzle );
@property( specular_map && !fresnel_workflow )
float3 fff = SampleSpecular( textureMaps@value( specular_map_idx ),
samplerState@value(specular_map_sampler),
UV_SPECULAR( inPs.uv@value(uv_specular).xy ),
texIndex_specularIdx ).xyz;
fff.g = fff.r;
fff.b = fff.r;
pixelData.specular.xyz *= fff;
@end
@property( specular_map && fresnel_workflow )
pixelData.F0 *= SampleSpecular( textureMaps@value( specular_map_idx ),
samplerState@value(specular_map_sampler),
UV_SPECULAR( inPs.uv@value(uv_specular).xy ),
texIndex_specularIdx ).@insertpiece( FresnelSwizzle );
@end
@else
float metalness = material.F0.x;
@property( specular_map )
float3 ddd = SampleSpecular( textureMaps@value( specular_map_idx ),
samplerState@value(specular_map_sampler),
UV_SPECULAR( inPs.uv@value(uv_specular).xy ),
texIndex_specularIdx ).xyz;
ddd.g = ddd.r;
ddd.b = ddd.r;
pixelData.specular.xyz *= ddd;
@end
pixelData.F0 = lerp( make_float_fresnel( 0.03f ), pixelData.diffuse.xyz * 3.14159f, metalness );
pixelData.diffuse.xyz = pixelData.diffuse.xyz - pixelData.diffuse.xyz * metalness;
@property( hlms_alphablend || hlms_screen_space_refractions )
pixelData.F0 *= material.F0.w; ///Should this be done for non-metallic as well???
@end
@end
@property( transparent_mode || hlms_screen_space_refractions )
pixelData.F0 *= pixelData.diffuse.w;
@end
@end
4、 pbr材质中的漫反射贴图不可以全黑或者过黑,也就是像素的亮度接近于0,在shader中我们可以看到, 基本所有漫反射(环境光、漫反射光)相关的材质都会乘以漫反射贴图的颜色,所以如果贴图颜色过暗则直接导致物体显示偏暗,另外漫反射贴图也不可出现高对比度。否则亮的部分在强光下容易过度曝光导致发白。所以漫反射贴图一定要亮度适中,避免高对比度。
上左图中暗部无法被任何光源照亮,右图为ps增加亮度后的结果
更多Ogre学习交流,欢迎加Q群:26自36己25去掉570