3D LUT 滤镜 shader 源码分析

news2024/11/16 2:32:27

最近在做滤镜相关的渲染学习,目前大部分 LUT 滤镜代码实现都是参考由 GPUImage 提供的 LookupFilter 的逻辑,整个代码实现不多。参考网上的博文也有各种解释,参考了大量博文之后终于理解了,所以自己重新整理了一份,方便以后阅读理解,对整体代码的实现过程结合LUT的原理进行一个简单整理。

GPUImageLookupFilter shader 源码

 varying highp vec2 textureCoordinate;      
 varying highp vec2 textureCoordinate2;
 
 uniform sampler2D inputImageTexture;  // 目标纹理,对应原始资源
 uniform sampler2D inputImageTexture2; // 查找表纹理,对应LUT图片
 
 uniform lowp float intensity;
 
 void main()
 {
     //获取原始图层颜色
     highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
     
     //获取蓝色通道颜色,textureColor.b 的范围为(0,1),blueColor 范围为(0,63) 
     highp float blueColor = textureColor.b * 63.0;
     
     //quad1为查找颜色所在左边位置的小正方形
     highp vec2 quad1;
     quad1.y = floor(floor(blueColor) / 8.0);
     quad1.x = floor(blueColor) - (quad1.y * 8.0);
     
     //quad2为查找颜色所在右边位置的小正方形
     highp vec2 quad2;
     quad2.y = floor(ceil(blueColor) / 8.0);
     quad2.x = ceil(blueColor) - (quad2.y * 8.0);
     
     //获取到左边小方形里面的颜色值
     highp vec2 texPos1;
     texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
     texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
     
    //获取到右边小方形里面的颜色值
     highp vec2 texPos2;
     texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
     texPos2.y = (quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);
     
     //获取对应位置纹理的颜色 RGBA 值
     lowp vec4 newColor1 = texture2D(inputImageTexture2, texPos1);
     lowp vec4 newColor2 = texture2D(inputImageTexture2, texPos2);
     
     //真正的颜色是 newColor1 和 newColor2 的混合
     lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));

     gl_FragColor = mix(textureColor, vec4(newColor.rgb, textureColor.w), intensity);
 }

整个源码的主要逻辑为:查找颜色所在位置的小正方形、查找小正方形内的具体颜色、颜色混合。上面注释已将具体的实现过程描述清楚,但与我们的 LUT 图片割裂,接下来结合 LUT 的实现原理以及具体的数据来形象地描述整个实现流程。

假设我们输入的参数为:
textureColor = ver4(.0, .0, 0.5, 1.0)

查找颜色所在位置的小正方形

我们知道LUT有64个小正方形,目标是为了找到对应小正方形里面的对应的颜色,我们需要先确认是第几个小正方形,正是通过 textureColor.b * 63 查找

带入blueColor -> textureColor.b = 0.5
textureColor.b * 63.0 = 31.5

也就是说我们需要第 [31.5] 位置小正方形,但是索引(从0-63共64个)都是正数,对于 31.5 索引 我们该怎么确定是 31 还是第 32 个呢?GPUImage给出的一种插值方式就是两个都要,然后进行一次混合,从而使得值能够俊均匀的在两个小正方形色块中。

具体逻辑为:

quad1.y = floor(floor(blueColor) / 8.0) = 3,确定为小方块在纵坐标索引3,也就是第4行。

quad1.x = floor(blueColor) - (quad1.y * 8.0) = 31 - 24 = 7

也就确定了小方块为(3,7) 也就是第4排第8个。

同理,对于第2个小方块确定的位置为(4,0) 也就是第5排第1个。

quad2.y = floor(ceil(blueColor) / 8.0) = 4

quad2.x = ceil(blueColor) - (quad2.y * 8.0)= 0

查找小正方形内的具体颜色

已经获取到对应的方块了,接下来需要确定方块内的像素的位置了。一般一个LUT的大小为 512x512,由8x8小方块构成,也就是每个方块的的像素为64x64,如下图所示:

计算x坐标的逻辑为:

texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r)

这一段是相对比较难理解的,我们可以分几部分进行理解:

第一部分:(quad1.x * 0.125)

我们得到 quad1.x = 7,也就是第8列,*0.125将坐标转化在(0,1)之间,也就是得到在01坐标系内如图红线的位置。

第二部分:((0.125 - 1.0/512.0) * textureColor.r)

我们可以把它当成 (63.0/512.0)* textureColor.r , 63.0/512.0代表着一个512x512中每个小方块的64份数据(为什么是63?别忘了0的存在),textureColor.r 数据在 0-1之间,这样就能确认在第一部分结果基础之上的偏移值。

第三部分:0.5/512.0

这一部分主要是 +0.5 做四舍五入运算,为保证第512行取到的是511.5/512,第1行取到的是 0.5/512.0。

同理,计算y的坐标,以及计算另一个小正方形内的位置是一样的。

最后在通过对从两个小正方形获取到的颜色进行 mix,并返回给着色器,GPU再对原始图像进行每一个像素点绘制,从而实现滤镜的效果。

总结

LUT 对应的 Shader 执行过程主要为:查找颜色所在位置的小正方形、查找小正方形内的具体颜色、颜色混合,整个流程都比较好理解,但代码相对而言比较难理解,网上看了很多其他的大佬写的一些文章,最开始自己看的时候也是很难理解的,后面终于悟了,所以想通过自己的理解,尽力更形象地解释(虽然可能也没有很形象),如果还有什么疑问,欢迎一起交流学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1151274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RT-Smart 开发笔记:int 类型数值溢出造成的奇怪问题的分析与排查记录

前言 最近在调试 RT-Smart 上的用户态 mq(消息队列)时,遇到一个奇怪的问题,这个例程打印了一下获取的时间,就可以正常的工作(超时退出),否则,就一直卡住(无法…

10.27~10.29数电第三次实验分析与问题

实验要求 分析 寄存器 D触发器有两个输出口,一个输入口,一个时钟信号,一个复位信号 同步异步就是说复位信号在不在always里 给它加一个load就成了一位寄存器, 寄存器堆 8个8位的寄存器堆,每个寄存器都有两读一写…

Zynq-Linux移植学习笔记之64- 国产ZYNQ在linux下配置国产5396芯片

1、背景介绍 复旦微ZYNQ通过SPI配置国产JEM5396,框图如下: 现在需要在linux下的应用程序内配置JEM5396的寄存器。其中FMQL和进口的XILINX ZYNQ类似,JEM5396和进口的BCM5396兼容。因此可以参考进口ZYNQ在linux下配置BCM5396过程。Zynq-Linux移…

【Java 进阶篇】Java HTTP响应消息详解

在Web开发中,HTTP(Hypertext Transfer Protocol)是一种用于传输数据的协议,它用于浏览器和Web服务器之间的通信。当你在浏览器中访问一个网页时,浏览器向Web服务器发送HTTP请求,然后Web服务器返回HTTP响应。…

YOLOv8如何添加注意力模块?

分为两种:有参注意力和无参注意力。 eg: 有参: import torch from torch import nnclass EMA(nn.Module):def __init__(self, channels, factor8):super(EMA, self).__init__()self.groups factorassert channels // self.groups > 0self.softmax …

CondaError_ Downloaded bytes did not match Content-Length

问题 使用anaconda下载包文件时,出现了CondaError: Downloaded bytes did not match Content-Length的错误 CondaError: Downloaded bytes did not match Content-Lengthurl: https://conda.anaconda.org/pytorch/win-64/pytorch-2.1.0-py3.11_cuda11.8_cudnn8_0.…

二维码智慧门牌管理系统升级,打造高效事件处理流程

文章目录 前言一、二维码智慧门牌管理系统的升级目标二、事件处理流程优化三、升级带来的好处 前言 随着城市化的不断推进,城市管理面临越来越多的挑战。为了更好地解决这些问题,许多城市已经开始采用二维码智慧门牌管理系统。这个系统不仅可以提高城市…

操作系统第一章-第三章大题_期末考试_详细易考

1.ABC三道作业如下表所示: 作业输入CPU输出A1505050B10060100C806050 (1) 计算在单道环境下运行时CPU的利用率;(2分) (2) 假设计算机系统中具有一个CPU、三个通道,画出ABC三道作业并发执行的情况图,并计算CPU利用率。(12分) 问题分析: c p u 利用率 c p u 有效…

Python对象(Object)与类型(Type)的关系

Object与Type 1、Object与Type概述2、Object与Type的关系 1、Object与Type概述 对象(Object)和类型(Type)是Python中两个最最基本的概念,它们是构筑Python语言大厦的基石 所有的数据类型,值,变…

[BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn

再补完这个就基本上完了. crypto RSA Variation II Schmidt-Samoa密码系统看上去很像RSA,其中Npqq, 给的eN给了d from secret import flag from Crypto.Util.number import *p getPrime(1024) q getPrime(1024)N p*p*qd inverse(N, (p-1)*(q-1)//GCD(p-1, q-1))m bytes…

cause: java.lang.numberformatexception: for input string

一个十分粗心的错误 我本来想要写的是name不为空,并且不为空字符串,结果不小心写成了空格! 解决方案:将空格改为空字符串即可

JMeter的使用——傻瓜式学习【中】

目录 前言 1、JMeter参数化 1.1、什么是参数化 1.2、用户定义的变量 1.2.1、什么时候使用用户定义的变量 1.2.2、使用“用户定义的变量”进行参数化的步骤: 1.2.3、案例 1.3、用户参数 1.3.1、什么时候使用用户参数? 1.3.2、使用“用户参数”进…

交叉编译工具链(以STM32MP1为例)

1.什么是交叉编译工具链? 在一个系统上进行编译,在另一个系统上进行执行 2.STM32MP1交叉编译工具链 3.交叉编译器内容 4.两种工具链模式 5.两种链接模式 6.工具使用 注意:OpenSTLinux已经提供了编译框架,不需要命令行手工编译 …

Spring Cloud 实战 | 解密Feign底层原理,包含实战源码

专栏集锦,大佬们可以收藏以备不时之需 Spring Cloud实战专栏:https://blog.csdn.net/superdangbo/category_9270827.html Python 实战专栏:https://blog.csdn.net/superdangbo/category_9271194.html Logback 详解专栏:https:/…

在VM虚拟机上安装centos并了解Linux常用命令

一. centos安装 新建一个虚拟机,使用ISO映像文件(在浏览器上直接搜索阿里云镜像站,下载合适的镜像文件) 安装后设置密码然后重启 重启后输入账号和密码 查看IP 输入命令: vi ifcfg-ens33,进入编辑界面&a…

物联网AI MicroPython传感器学习 之 PAJ7620手势识别传感器

学物联网,来万物简单IoT物联网!! 一、产品简介 手势识别传感器PAJ7620u2是一款集成3D手势识别和运动跟踪为一体的交互式传感器,传感器可以在有效范围内识别手指的顺时针/逆时针转动方向和手指的运动方向等。它可以识别13种手势&a…

STM32的RTC模块的难点推导

在 S T M 32 STM32 STM32的 R e a l t i m e c l o c k , R T C Real\quad time\quad clock,RTC Realtimeclock,RTC模块中有一些功能点不太好理解,下面我根据我自己对这些功能难点的理解来做一些推导并记录如下。 首先来看一下平滑数字校准。假设我们目前的 R …

万字解析设计模式之原型模式与建造者模式

一、原型模式 1.1概述 原型模式是一种创建型设计模式,其目的是使用已有对象作为原型来创建新的对象。原型模式的核心是克隆,即通过复制已有对象来创建新对象,而不是通过创建新对象的过程中独立地分配和初始化所有需要的资源。这种方式可以节…

CMT2310A一款低功耗高性能Sub-1GHz射频收发器芯片

CMT2310A是一款超低功耗,高性能,适用于各种113至960 MHz无线应用的00K,(G)FSK 和4(G)FSK 射频收发器。它是CMOSTEK NextGenRFTM 射频产品线的一部分,这条产品线包含完整的发射器,接收器和收发器。CMT2310A的高集成度,简…

npm package.json属性详解

npm package.json属性详解 概述 package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象。其中很多属性可以通过npm-config来生成 name package.json中最重要的属性是name和version两个属性,这两个属性是必须要有的,否则模块就…