问题描述
我想编写一些着色器(表面/碎片/...)将我的漫反射纹理重新上色为某种新颜色。目前我有这个版本的着色器(我正在尝试实时重新着色纹理):
//sm_surf
uniform vec4 colorTarget; (used in full version)
uniform vec4 colorTint; (used in full version)
vec4 colorTexture = texture2D(u_diffuseTexture, _surface.diffuseTexcoord);
//vec4 colorTexture = _sample.diffuse;//the same result
vec4 tinted = colorTexture;
tinted.r = max(0.0, colorTexture.r - 0.2);
_surface.diffuse = tinted;
这是OpenCV中的代码(我只是事先对纹理重新着色,并将其用作新的漫反射纹理):
image = cv::imread([path UTF8String], cv::IMREAD_UNCHANGED);
for (int i = 0; i < image.rows; i++) {
for (int j = 0; j < image.cols; j++) {
cv::Vec4b pixel = image.at<cv::Vec4b>(i,j);
pixel[2] = fmin(255, fmax(0, pixel[2] - 50));
image.at<cv::Vec4b>(i,j) = pixel;
}
}
cv::imwrite([newPath UTF8String], image);
对于此测试,我只想减少颜色的R分量。结果:
OpenCV(正确)
场景工具包(错误)
漫反射纹理包含Alpha通道。(按映像解决)此外,似乎重新着色后着色器的Alpha通道已损坏。使用此着色器:
tinted.r = 1;
tinted.g = 0;
tinted.b = 0;
如何才能像在OpenCV中一样对漫反射纹理重新着色?
更新:这是针对SceneKit着色器和OpenCV的结果(我已从图像中删除所有透明像素):
着色器:
vec4 colorTexture = _surface.diffuse;
vec3 tinted = colorTexture.a > 0.0 ? colorTexture.rgb / colorTexture.a : colorTexture.rgb;
if (colorTexture.a == 1) {
tinted.r = max(0.0, colorTexture.r - 0.2);
} else {
colorTexture.a = 0;
}
_surface.diffuse = vec4(tinted, 1.0) * colorTexture.a;
和OpenCV代码:
pixel[2] = fmax(0, pixel[2] - 50);//2 because it's bgr in OpenCV
if (pixel[3] != 255) {
pixel[3] = 0;
}
一些更奇怪的事情:我已将OpenCV代码更改为此代码以生成新纹理
pixel[0] = 255 - (j % 4) * 30;//b
pixel[1] = 0;//g
pixel[2] = 0;//r
pixel[3] = 255;
如果我这样更改此纹理:
if (pixel[0] == 255) {
pixel[0] = 255;pixel[1] = 255;pixel[2] = 255;
} else {
pixel[0] = 0;pixel[1] = 0;pixel[2] = 0;
}
我收到的SMTH是这样的:
此SceneKit着色器应该是相同的:
vec4 colorTexture = _surface.diffuse;
vec3 tinted = colorTexture.rgb; // colorTexture.a == 1
if (tinted.b > 0.99) {
tinted = vec3(0,0,0);
} else {
tinted = vec3(1,1,1);
}
_surface.diffuse = vec4(tinted, 1.0) * colorTexture.a;
但我收到的是:
有一些白色条纹,但太细了。
我可以通过将条件更改为着色来增加它们。b>0.85,但这已经是一个错误了,因为颜色in_Surface。漫反射与纹理中的颜色不同。似乎是SceneKit对纹理或SMITH进行了插补。更新2:
我已将source code(1.5Mb)添加到此问题中。有3个球体:1)具有原始纹理
左2)纹理由着色器重新着色(Newr=r-0.2)(浮动)
3右),纹理由OpenCV重新着色(Newr=r-51)(Uint8)
它们是不同的!场景不包含任何灯光/环境/...只有3个球。
推荐答案
SceneKit使用预乘的Alpha。以下操作应该有效:
vec3 tinted = colorTexture.a > 0.f ? colorTexture.rgb / colorTexture.a : colorTexture.rgb;
tinted.r = max(0.0, tinted.r - 0.2);
_surface.diffuse = vec4(tinted, 1.f) * colorTexture.a;
编辑
此代码在您所附的示例中工作得很好,但需要进行一些更改才能使两种技术100%匹配。如this other SO thread中所述,SceneKit着色器在线性颜色空间中运行。
这意味着您的CPU代码中的51
不会映射到您的GPU代码中的0.2
。若要获得相同的结果,您需要将采样颜色(线性)转换为着色颜色(非线性),应用色调操作,然后转换回线性。
对于带有条纹的示例,这是预期的行为。Mipmapping将导致灰度值。如果您移动到离对象足够近的位置,以便将1个纹理像素投影到屏幕上的~1个像素上,那么您将只能再次获得黑白值。
这篇关于SceneKit:使用着色器重新着色漫反射纹理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!