我编写了一个简单的3D模型查看器(使用OpenGL 3.2和GLSL),可以加载和渲染U3D模型(独立或从PDF内部)。它基本上可以渲染所有不同的网格,通过顶点颜色,纹理或材质绘制它们并相应地点亮它们。
然而,我还没有弄清楚结合材料和纹理的“预期方式”是什么。我已经看过教程,其中包含具有材质的物体(具有漫反射/镜面反射/环境成分以及不透明度和光泽度的因素),以及其他使用纹理并点亮它们的教程,但我没有找到任何预期的好资源行为是它们组合时(即网格物体有一个材质和一个或多个纹理层)。
另外根据他们的“LitTextureShader”描述中U3D文件格式的规范,我支持4种不同的混合函数,它们决定了一个纹理层如何与前面的结果相结合:
// Multiply
blended = current * previous;
// Add
blended = clamp(current + previous, 0.0, 1.0);
// Replace
blended = current;
// Blend
blended = mix( previous , current, current.a );
最初我认为这只是在纹理图层之间应用,但是当我在Adobe Reader中查看模型时,这些混合操作似乎也会在纹理和材质之间应用,但结果永远不会是我期望的结果它是(并且看起来与我的结果非常不同),所以我认为有一些我不完全理解的潜在部分。
对于我的测试,我创建了一个由单个三角形,红色材质和两个棋盘纹理组成的网格:
和
我的片段着色器通常看起来像这样(缩短和简化,不考虑镜面反光或反射光):
// uniforms, lights etc
// ...
// material input
flat in vec4 matDiffuse;
flat in float matOpacity;
// ...
// globals
vec3 accumulatedLight = vec3(0.0);
vec4 currAmbientColor = vec4(0.0);
vec4 currDiffuseColor = vec4(0.0);
float currOpacity = 1.0;
// ...
// lighting
void AddLightOutput()
{
// add ambient
vec3 ambientTerm = vec3(0.0);
if( Lights.enableAmbient == 1 )
{
ambientTerm = (vec3(currAmbientColor) * vec3(Lights.ambientIntensity));
}
// add diffuse and specular
vec3 diffuseTerm = vec3(0.0);
for( int index = 0; index < NUMBER_OF_LIGHTS; index++ )
{
// calculate ...
diffuseTerm += vec3(lgt.diffuse) * lightDot * attenuation;
// specular (disregard for now)
// ...
}
// add up results results
accumulatedLight = ambientTerm + (diffuseTerm * vec3(currDiffuseColor));
}
void main()
{
// set up colors and opacity
currDiffuseColor = texture(texSampler0, vertTexCoord0) * vec4( vec3(texIntensity0), 1.0 );
currOpacity = matOpacity;
// apply material-texture interaction ?
// ??
// iterate all lights
AddLightOutput();
// set result color
resultColor = vec4(accumulatedLight, currOpacity);
}
[侧面问题:我将所有材质保留在纹理缓冲区对象中,并且顶点着色器根据制服拾取材质,并将其传递给片段着色器(展平/展开) - 不确定是否是好主意(我加载的一些模型有几百个甚至超过一千种材料,这就是我尝试这种方式的原因)?]
基于上面的着色器,我尝试了两种情况,一种是单个纹理(蓝绿色棋盘)与红色材质相结合,第一种情况下的纹理混合设置为“乘法”并且在第二个是“添加”(纹理和材料都不是半透明的)
我的着色器:
// add base color info
currDiffuseColor = texture(texSampler0, vertTexCoord0) * vec4( vec3(texIntensity0), 1.0 );
// modify color according to blend factors
currOpacity = matOpacity * currDiffuseColor.a;
currDiffuseColor = matDiffuse * currDiffuseColor;
这让我相信我在正确的轨道上并且材料和纹理都应该被操作以获得正确的结果,但是然后“添加”着色器:
// add base color info
currDiffuseColor = texture(texSampler0, vertTexCoord0) * vec4( vec3(texIntensity0), 1.0 );
// modify color according to blend factors
currOpacity = matOpacity;
currDiffuseColor = vec4( clamp(matDiffuse + currDiffuseColor, 0.0, 1.0).rgb, currDiffuseColor.a );
结果:
这让我觉得这些材料不应该影响它。所以我也尝试了两个纹理(那些没有透明度btw的棋盘格)的情况应该互相添加:
// set up colors and opacity
currDiffuseColor = texture(texSampler0, vertTexCoord0) * vec4( vec3(texIntensity0), 1.0 );
vec4 texLayer1 = texture(texSampler1, vertTexCoord1) * vec4( vec3(texIntensity1), 1.0 );
currDiffuseColor = vec4( clamp(currDiffuseColor + texLayer1, 0.0, 1.0).rgb, texLayer1.a );
我的代码将它们加在一起,而Adobe Reader似乎只是显示纹理层0。
另外例如,如果我设置U3D使纹理图层透明(并使用混合常量),Adobe会给我这个:
但是Adobe似乎没有考虑混合常量的值,所以我的结果也不同。
因此,所有这些结果让我感到困惑,因为“正常”的纹理和材料应该如何相互影响。本质上,结果不一定必须与Adobe完全相同,但我希望得到可预测的结果,以便制作模型的人可以根据建模或渲染软件的“典型行为”知道会发生什么。
如果我有材质不透明度和纹理不透明度,它们会相互叠加吗?是否只有纹理图层可以相互混合/相加/相互/相互替换,还是与材质相互作用?材料是照明的唯一因素吗?或者应该在纹理上计算光线?对我来说,似乎Adobe的结果不一致,但可能只是我缺乏一些理解。
作为参考,我已将我创建的示例PDF上传到此zip:https://s3.amazonaws.com/drabeck/stackoverflow/TestPDF3Ds.zip - 它还包含从我的应用程序生成的屏幕截图,以及我用于创建U3D的文本文件(IDTF)(从那起PDF)。