Unity原生OpenGL贴图显示四次
Unity native OpenGL texture displayed four times
我目前正面临一个我根本不明白的问题。
我使用 ARCore 执行由内而外的跟踪任务。因为我需要做一些额外的图像处理,所以我使用 Unitys 功能来加载本机 c++ 插件。在每一帧的最后,我将 YUV_420_888 格式的图像作为原始字节数组传递给我的原生插件。
在组件初始化开始时创建纹理句柄:
private void CreateTextureAndPassToPlugin()
{
Texture2D tex = new Texture2D(640, 480, TextureFormat.RGBA32, false);
tex.filterMode = FilterMode.Point;
tex.Apply();
debug_screen_.GetComponent<Renderer>().material.mainTexture = tex;
// Pass texture pointer to the plugin
SetTextureFromUnity(tex.GetNativeTexturePtr(), tex.width, tex.height);
}
由于我只需要灰度图,所以基本上忽略了图片的UV部分,只使用y坐标,如下所示:
uchar *p_out;
int channels = 4;
for (int r = 0; r < image_matrix->rows; r++) {
p_out = image_matrix->ptr<uchar>(r);
for (int c = 0; c < image_matrix->cols * channels; c++) {
unsigned int idx = r * y_row_stride + c;
p_out[c] = static_cast<uchar>(image_data[idx]);
p_out[c + 1] = static_cast<uchar>(image_data[idx]);
p_out[c + 2] = static_cast<uchar>(image_data[idx]);
p_out[c + 3] = static_cast<uchar>(255);
}
}
然后每一帧图像数据被放入一个GL纹理中:
GLuint gltex = (GLuint)(size_t)(g_TextureHandle);
glBindTexture(GL_TEXTURE_2D, gltex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 640, 480, GL_RGBA, GL_UNSIGNED_BYTE, current_image.data);
我知道我通过创建和传递纹理作为 RGBA 使用了太多内存,但是由于 OpenGL ES3 不支持 GL_R8 并且 GL_ALPHA 总是导致内部 OpenGL 错误我只是通过每个颜色分量的灰度值。
然而最终纹理渲染如下图所示:
起初我想,这可能是因为其他通道具有相同的值,但是将除第一个通道之外的所有其他通道设置为任何值都没有任何影响。
我是否遗漏了一些 OpenGL 纹理创建方面的知识?
YUV_420_888 是多平面纹理,其中亮度平面每个像素仅包含一个通道。
for (int c = 0; c < image_matrix->cols * channels; c++) {
unsigned int idx = r * y_row_stride + c;
您的循环边界假设 c
是 4 个通道的倍数,这适用于输出表面,但您随后在计算输入表面索引时也会使用它。您使用的输入表面平面仅包含一个通道,因此idx
是错误的。
通常,您还会多次覆盖同一内存 - 循环每次迭代都会将 c
递增 1,但您随后会写入 c
、c+1
、c+2
, 和 c+3
所以覆盖你上次写的三个值。
更简短的回答 - 您的 OpenGL ES 代码很好,但我认为您正在用错误的数据填充纹理。
未经测试,但我认为您需要:
for (int c = 0; c < image_matrix->cols * channels; c += channels) {
unsigned int idx = (r * y_row_stride) + (c / channels);
我目前正面临一个我根本不明白的问题。 我使用 ARCore 执行由内而外的跟踪任务。因为我需要做一些额外的图像处理,所以我使用 Unitys 功能来加载本机 c++ 插件。在每一帧的最后,我将 YUV_420_888 格式的图像作为原始字节数组传递给我的原生插件。
在组件初始化开始时创建纹理句柄:
private void CreateTextureAndPassToPlugin()
{
Texture2D tex = new Texture2D(640, 480, TextureFormat.RGBA32, false);
tex.filterMode = FilterMode.Point;
tex.Apply();
debug_screen_.GetComponent<Renderer>().material.mainTexture = tex;
// Pass texture pointer to the plugin
SetTextureFromUnity(tex.GetNativeTexturePtr(), tex.width, tex.height);
}
由于我只需要灰度图,所以基本上忽略了图片的UV部分,只使用y坐标,如下所示:
uchar *p_out;
int channels = 4;
for (int r = 0; r < image_matrix->rows; r++) {
p_out = image_matrix->ptr<uchar>(r);
for (int c = 0; c < image_matrix->cols * channels; c++) {
unsigned int idx = r * y_row_stride + c;
p_out[c] = static_cast<uchar>(image_data[idx]);
p_out[c + 1] = static_cast<uchar>(image_data[idx]);
p_out[c + 2] = static_cast<uchar>(image_data[idx]);
p_out[c + 3] = static_cast<uchar>(255);
}
}
然后每一帧图像数据被放入一个GL纹理中:
GLuint gltex = (GLuint)(size_t)(g_TextureHandle);
glBindTexture(GL_TEXTURE_2D, gltex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 640, 480, GL_RGBA, GL_UNSIGNED_BYTE, current_image.data);
我知道我通过创建和传递纹理作为 RGBA 使用了太多内存,但是由于 OpenGL ES3 不支持 GL_R8 并且 GL_ALPHA 总是导致内部 OpenGL 错误我只是通过每个颜色分量的灰度值。
然而最终纹理渲染如下图所示:
起初我想,这可能是因为其他通道具有相同的值,但是将除第一个通道之外的所有其他通道设置为任何值都没有任何影响。
我是否遗漏了一些 OpenGL 纹理创建方面的知识?
YUV_420_888 是多平面纹理,其中亮度平面每个像素仅包含一个通道。
for (int c = 0; c < image_matrix->cols * channels; c++) {
unsigned int idx = r * y_row_stride + c;
您的循环边界假设 c
是 4 个通道的倍数,这适用于输出表面,但您随后在计算输入表面索引时也会使用它。您使用的输入表面平面仅包含一个通道,因此idx
是错误的。
通常,您还会多次覆盖同一内存 - 循环每次迭代都会将 c
递增 1,但您随后会写入 c
、c+1
、c+2
, 和 c+3
所以覆盖你上次写的三个值。
更简短的回答 - 您的 OpenGL ES 代码很好,但我认为您正在用错误的数据填充纹理。
未经测试,但我认为您需要:
for (int c = 0; c < image_matrix->cols * channels; c += channels) {
unsigned int idx = (r * y_row_stride) + (c / channels);