GraphicsLab Project之Color Processing

来源:互联网 发布:文网文添加域名 编辑:程序博客网 时间:2024/06/05 23:45

作者:i_dovelemon

日期:2016 / 09 / 15

来源:CSDN

主题:Post-processing, Gamma correction, Color Grading



引言



        今天来和大家分享下一种在屏幕空间对整体的画面进行(Color Grading)颜色调制的一种简单方法。这些方法在成熟的引擎中,都有运用。比如CryEngine中使用的大量滤镜,还有Unreal中Post-processing中的Color Grading就是实现这样的功能。


Color Grading



        熟悉PS的同学应该都知道,在PS中,如果我们需要对一张图片进行颜色,饱和度,对比度和曝光度这样的整体调节就是Color Grading。举个例来说,比如现在我们想要下面的效果:




        我们只要对原有的图像进行黑白滤镜处理就能够得到。上面仅仅是通过滤镜能够实现的其中一种效果,通过对PS中各个参数的调节,我们能够实现很多不同的效果,这些效果能够很容易的让美工对画面的整体风格进行把控,是一种很好的控制画面风格,实现各种特殊效果的方法。


如何在渲染中实现?



        首先,我们要知道Color Grading是对图片中的每一个像素的颜色进行操作,即仅仅与像素的颜色有关系,而与像素所处的位置,附近的颜色等等没有关系。读者请注意,这里说的是最终进行Color Grading时进行的操作对象,而操作中各个参数的来源是需要考虑整幅图片的属性的。比如说,我要调节一下整个场景中的颜色亮度,那么我需要先计算一下场景的平均亮度值,然后根据这个值对每一个像素进行操作。

        从上面的描述中,大家可能也看到了,我在这里提出的实现,仅仅是对每一个像素进行最终的Color Grading操作,而操作的来源却没有实现。那么我该怎么得到这个操作的来源了?这个问题就是我们Color Grading实现的核心。

        在Unreal中,提供了一个内置的编辑器,能够实时的调整例如颜色,对比度,饱和度这样的参数,然后使用这些参数去进行Color Grading操作。也就是说,我们需要一种手段,能够得到这些参数,然后在我们的Shader中进行计算。但是,同学们,这里的计算方法多种多样,看看PS里面提供的各种效果你就知道了。我们不太可能把每一种计算方式编码到shader中去。

        在了解了上面的困境之后,我们该怎么解决这个问题了?上面说了,计算的过程不太可能放在shader中,那么我们直接将计算的结果放在shader中如何?前面讨论过,最终的操作是对每一个像素进行,所以也就是说,RGB(0-255,0-255,0-255)这个范围里面的所有颜色,都能够在最终的结果里面找到对应的颜色值。那么,我们为什么不构造一张最终结果的查找表了?然后通过源颜色值,从查找表中查找到最终进行Color Grading之后的颜色。这样不就是把所有的计算操作放在了外部,比如PS软件中,我们在渲染的时候,仅仅需要查询下最终的结果就可以了。

        从上面的讨论,大家应该就大致的了解到了如何使用这样的方式进行Color Grading了吧?


Color Grading的步骤



        为了让大家更清楚明白,这里详细的列举进行Color Grading的步骤。

        1.首先你得将RGB(0-255,0-255,0-255)颜色做成一张查找表,便于我们进行查找。由于最终的查找操作是在shader中进行的,所以这张查找表最好的方式还是使用贴图。这里提出两中方法供大家参考。在说明之前,我们先来看看如果将所有的颜色都保存下来,大概需要多大的空间:

        Mem = 256 * 256 * 256 * 4 / 1024 / 1024 = 64MB

        可以看出,将所有的颜色保存下来需要64MB的一张贴图,先不说支不支持这么大的贴图,单单这个内存就吃不消。(当然,如果你只要一种Color Grading效果的话,又很土豪的话,保持精度,也可以这么做。)所以在实际开发中,我们往往选择16*16*16的图,或者32*32*32的图。虽然颜色损失了一点,但是通过线性插值也能够逼近实际的效果。下面列举构建查找表的两中方式(以下示例,都以16×16×16的尺寸进行描述)

                a.将所有的颜色保存在一个2D纹理中,保存的最终图像如下所示:



                b.将所有颜色保存在一个3D纹理中,保存的最终图像如下所示:




        2.截取游戏中的一张图片,然后将你所使用的查找表和游戏截图放在一起,通过PS软件更改两个图层的颜色,对比度等等属性,达到你想要的效果。

        3.通过上面的操作,你原来的查找表,就变成了进行Color Grading之后的查找表。然后使用这张查找表到Shader中进行实际的计算。计算方法很简单,只要在显示画面之前,添加一个最后的render pass,在这个pass中使用你计算好的颜色值,以这个值为坐标,查找纹理中对应的颜色就可以了。


OpenGL实现



        上面的理论知识讲解完毕之后,我们来点实际的代码。首先,我选择的是16×16×16的3D纹理。选择的原因,一方面是因为我渲染出来的画面大小容纳不下32*32*32的贴图,所以没有办法很好的在PS中进行调色,另外一方面,3D纹理查找更加的直接,如果使用2D纹理,还需要对纹理坐标进行一些计算,比较麻烦。

        在OpenGL中创建3D纹理的方法,如下所示:

glBindTexture(GL_TEXTURE_3D, tex_obj);glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, texture_width, texture_width, texture_width, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);glGenerateMipmap(GL_TEXTURE_3D);glBindTexture(GL_TEXTURE_3D, 0);

        这里需要注意几个地方:

        1.OpenGL的纹理坐标是u向右为正方向,v向上为正方向,和DX是有区别的,所以在构建查找表的时候,需要注意

        2.OpenGL中3D纹理创建,需要构建slice数组,然后传递到glTexImage3D中去。也就是说后面的texture_data实际上是3D纹理贴图里面的每一个小的2D纹理贴图的数组。

        3.OpenGL中在shader设置3D纹理坐标的时候,需要注意一些东西。下面给出的Shader中有一个链接,详细的讲解了这个知识。

        对应的shader文件:

//------------------------------------------------------------// Declaration: Copyright (c), by i_dovelemon, 2016. All right reserved.// Author: i_dovelemom[1322600812@qq.com]// Date: 2016 / 09 / 12// Brief: Color process will do color correction and color grading//------------------------------------------------------------in vec2 glb_ps_tex_coord;out vec3 glb_color;uniform sampler2D glb_scene_tex;uniform sampler3D glb_lut32_tex;const float glb_gamma = 2.2;const float glb_lut_size = 16.0;void main() {vec3 src_color = texture2D(glb_scene_tex, glb_ps_tex_coord).xyz;// Color correction (aka Gamma correction)float gamma = 1.0 / glb_gamma;src_color.x = pow(src_color.x, gamma);src_color.y = pow(src_color.y, gamma);//----------------------------------------------------------------------// The z value is a little tricky.// If the 3D texture is 16*16*16,// then the first slice's z coordinate is defined as 0.5 * 1.0 / 16.0.// This means if you don't specific the z coordinate as center the slice,// it will sample two nearest slice and blend the result value.// For more detail, look at this page:// http://stackoverflow.com/questions/31810822/opengl-3d-texture-shine-through//----------------------------------------------------------------------src_color.z = pow(src_color.z, gamma);src_color.z = src_color.z / (1.0 / glb_lut_size);src_color.z = floor(src_color.z) * 1.0 / glb_lut_size + 0.5 * 1.0 / glb_lut_size;// Color gradingglb_color = texture3D(glb_lut32_tex, src_color).xyz;}

        这个shader做了两件事。第一件事是进行Gamma correction,这样能够让我们的计算在线性空间中进行。关于这方面的内容,可以参考之前博客中的介绍。第二件事就是进行Color Grading。这个操作很简单,就是对3D纹理,进行一下查找,然后使用对应的颜色来替换就可以了。


总结



        这种方法具有很大的取巧性,如果想要深入的了解Color Grading的实质,可以自己在shader中实现各种各样的算法,加强自己的能力。


参考文献


[1]  http://www.kpulv.com/359/Dev_Log__Color_Grading_Shader/ Dev_Log: Color Grading Shader
[2] http://the-witness.net/news/2012/08/fun-with-in-engine-color-grading/ Fun with in-engine Color Grading
[3] http://renderwonk.com/publications/s2010-color-course/hoffman/hoffman_course_notes_intro.pdf Color Enhancement and Rendering in Film and Game Production
0 0
原创粉丝点击