Scenekit:使用着色器为漫反射纹理重新着色

小丑

我想编写一些着色器(表面/碎片/ ...),以便以某种新颜色重新着色漫反射纹理。当前,我具有此版本的着色器(我正在尝试实时重新着色纹理):

//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(正确

OpenCV图像

SceneKit(不正确

SceneKit图片

漫反射纹理包含Alpha通道。

(由小工具解决)而且,好像在重新着色着色器Alpha通道后。使用此着色器:

tinted.r = 1;
tinted.g = 0;
tinted.b = 0;

我的结果是:场景套件

代替:OpenCV的

原始纹理: 原版的

How can I just recolor diffuse texture like in openCV?


UPDATE: This are result for SceneKit shader and OpenCV (I have removed all transparent pixels from image):

OpenCV: openCV

SceneKit: 场景套件

shader:

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;

and OpenCV code:

pixel[2] = fmax(0, pixel[2] - 50);//2 because it's bgr in OpenCV
if (pixel[3] != 255) {
    pixel[3] = 0;
}

Some more strange things: I have changed my OpenCV code to this to generate new texture

pixel[0] = 255 - (j % 4) * 30;//b
pixel[1] = 0;//g
pixel[2] = 0;//r
pixel[3] = 255;

If I change this texture like this:

if (pixel[0] == 255) {
    pixel[0] = 255;pixel[1] = 255;pixel[2] = 255;
} else {
    pixel[0] = 0;pixel[1] = 0;pixel[2] = 0;
}

I receive smth like this:

OpenCV的

With this SceneKit shader it should be the same:

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;

But I receive this:

在此处输入图片说明

There are some white stripes, but way too thin.

I can increase them by changing condition to tinted.b > 0.85, but it's already an error, because color in _surface.diffuse not the same as in texture. Seems like SceneKit interpolates the texture or smth like that.


UPDATE2:

I have added source code (1.5Mb) with this problem. There are 3 spheres:

1 Top) With original texture

2 Left) With texture recolored by shader (newR = r - 0.2) (float)

3 Right) With texture recolored by OpenCV (newR = r - 51) (uint8)

and they are different! Scene doesn't contain any light/env/... just 3 spheres.

mnuages

SceneKit uses premultiplied alpha. The following should work:

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;

Edit

This code works well in the example you attached, but requires some changes for the two techniques to match 100%. As explained in this other SO thread, SceneKit shaders operate in a linear color space.

This means that 51 in your CPU code doesn't map to 0.2 in your GPU code. To get the same result you will need to convert the sampled color (linear) to a tinted color (non-linear), apply the tint operation, and then convert back to linear.


对于带有条纹的示例,这是预期的行为。Mipmapping将导致灰度值。如果您移动到离对象足够近的位置,从而将1个纹理像素投影到屏幕上的〜1像素上,那么您只会再次获得黑白值。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章