Scenekit:使用着色器重新着色漫反射纹理
Scenekit: recolor diffuse texture with shader
我想编写一些着色器 (surface/fragment/...) 以某种新颜色重新着色我的漫反射纹理。目前我有这个版本的着色器(我正在尝试实时重新着色纹理):
//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(正确)
SceneKit(不正确)
漫反射纹理包含 alpha 通道。
(由 mnuages 解决)
此外,似乎在重新着色着色器 alpha 通道后被破坏。使用此着色器:
tinted.r = 1;
tinted.g = 0;
tinted.b = 0;
我的结果是:
而不是:
原始纹理:
我怎样才能像在 openCV 中那样重新着色漫反射纹理?
更新:
这是 SceneKit 着色器和 OpenCV 的结果(我已经从图像中删除了所有透明像素):
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;
}
我收到这样的消息:
对于这个 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;
但我收到了这个:
有一些白色条纹,但是太细了。
我可以通过将条件更改为 tinted.b > 0.85 来增加它们,但这已经是一个错误,因为 _surface.diffuse 中的颜色与纹理中的颜色不同。似乎 SceneKit 会像那样插入纹理或 smth。
更新2:
我已经添加了 source code (1.5Mb) 这个问题。有 3 个球体:
1顶)有原图
2 左)纹理由着色器重新着色 (newR = r - 0.2) (float)
3 右)纹理由 OpenCV 重新着色 (newR = r - 51) (uint8)
而且他们不一样!场景不包含任何 light/env/...只有 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% 匹配。如 中所述,SceneKit 着色器以线性颜色运行 space。
这意味着 CPU 代码中的 51
不会映射到 GPU 代码中的 0.2
。
要获得相同的结果,您需要将采样颜色(线性)转换为着色颜色(非线性),应用着色操作,然后再转换回线性。
至于带有条纹的示例,这是预期的行为。 Mipmapping 将导致灰度值。如果你移动到离物体足够近,以至于 1 个纹素被投影到屏幕上的 ~1 个像素上,那么你将只会再次获得黑白值。
我想编写一些着色器 (surface/fragment/...) 以某种新颜色重新着色我的漫反射纹理。目前我有这个版本的着色器(我正在尝试实时重新着色纹理):
//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(正确)
SceneKit(不正确)
漫反射纹理包含 alpha 通道。
(由 mnuages 解决) 此外,似乎在重新着色着色器 alpha 通道后被破坏。使用此着色器:
tinted.r = 1;
tinted.g = 0;
tinted.b = 0;
我的结果是:
而不是:
原始纹理:
我怎样才能像在 openCV 中那样重新着色漫反射纹理?
更新: 这是 SceneKit 着色器和 OpenCV 的结果(我已经从图像中删除了所有透明像素):
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;
}
我收到这样的消息:
对于这个 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;
但我收到了这个:
有一些白色条纹,但是太细了。
我可以通过将条件更改为 tinted.b > 0.85 来增加它们,但这已经是一个错误,因为 _surface.diffuse 中的颜色与纹理中的颜色不同。似乎 SceneKit 会像那样插入纹理或 smth。
更新2:
我已经添加了 source code (1.5Mb) 这个问题。有 3 个球体:
1顶)有原图
2 左)纹理由着色器重新着色 (newR = r - 0.2) (float)
3 右)纹理由 OpenCV 重新着色 (newR = r - 51) (uint8)
而且他们不一样!场景不包含任何 light/env/...只有 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% 匹配。如
这意味着 CPU 代码中的 51
不会映射到 GPU 代码中的 0.2
。
要获得相同的结果,您需要将采样颜色(线性)转换为着色颜色(非线性),应用着色操作,然后再转换回线性。
至于带有条纹的示例,这是预期的行为。 Mipmapping 将导致灰度值。如果你移动到离物体足够近,以至于 1 个纹素被投影到屏幕上的 ~1 个像素上,那么你将只会再次获得黑白值。