美文网首页
相机之廋脸

相机之廋脸

作者: 0246eafe46bd | 来源:发表于2022-01-03 20:22 被阅读0次

相机之使用OpenGL预览
相机之使用OpenGL拍照
相机之使用OpenGL录像
相机之为录像添加音频
相机之大眼
相机之贴纸
相机之口红
相机之美颜磨皮
相机之LUT滤镜

相机廋脸效果

瘦脸效果实现:将指定圆形区域内的像素按照一定的规则进行整体偏移,越靠近圆心,像素偏移的强度越大,示意图:


jialing.jpeg

片段着色器

precision highp float;

varying vec2 v_TextureCoord;
uniform sampler2D vTexture;

// 圆半径
uniform float leftRadius;
// 拉伸中心坐标
uniform vec2 leftOrigin;
// 拉伸目标坐标
uniform vec2 leftTarget;

uniform float rightRadius;
uniform vec2 rightOrigin;
uniform vec2 rightTarget;

// textureCoord:采样坐标;originPosition:拉伸中心坐标;targetPosition:拉伸目标坐标;radius:拉伸半径;curve:拉伸算法参数
vec2 stretch(vec2 textureCoord, vec2 originPosition, vec2 targetPosition, float radius, float curve){
    // 存储当前采样点到应该采样的点的偏移值
    vec2 offset = vec2(0.0);
    // 应该采样的点
    vec2 result = vec2(0.0);
    // 偏移的方向
    vec2 direction = targetPosition - originPosition;
    // 采样坐标距离拉伸中心坐标越近,偏移越远
    float infect = distance(textureCoord, originPosition) / radius;
    // 放大infect的影响,默认curve为1,即不放大影响,这个值越大,拉伸到指定点越圆润,越小越尖
    infect = pow(infect, curve);
    infect = 1.0 - infect;
    infect = clamp(infect, 0.0, 1.0);
    // 要偏移的值
    offset = direction * infect;
    result = textureCoord - offset;
    return result;
}

void main(){
    vec2 stretchedCoord = stretch(v_TextureCoord, leftOrigin, leftTarget, leftRadius, 1.0);
    stretchedCoord = stretch(stretchedCoord, rightOrigin, rightTarget, rightRadius, 1.0);
    gl_FragColor = texture2D(vTexture, stretchedCoord);
}

着色器程序

class FaceLiftFilter(context: Context, width: Int, height: Int) :
    FboFilter(context, R.raw.base_vertex, R.raw.face_lift_frag, width, height) {
    private var faceLiftRatio: Float = 0f
    private lateinit var matrix: FloatArray
    private var facePosition: FloatArray = FloatArray(84 * 2)

    // 圆的半径,只有在圆内才会进行变形
    private val leftRadiusLocation = GLES20.glGetUniformLocation(mProgram, "leftRadius")

    // 拉伸中心坐标,相当于PS中使用液化功能时,鼠标点下的地方
    private val leftOriginLocation = GLES20.glGetUniformLocation(mProgram, "leftOrigin")

    // 拉伸目标坐标,相当于PS中使用液化功能时,鼠标抬起的地方
    private val leftTargetLocation = GLES20.glGetUniformLocation(mProgram, "leftTarget")

    private val rightRadiusLocation = GLES20.glGetUniformLocation(mProgram, "rightRadius")
    private val rightOriginLocation = GLES20.glGetUniformLocation(mProgram, "rightOrigin")
    private val rightTargetLocation = GLES20.glGetUniformLocation(mProgram, "rightTarget")

    // 左太阳穴索引
    private val leftTempleIndex = 63

    // 右太阳穴索引
    private val rightTempleIndex = 62

    // 下巴索引
    private val chinIndex = 64

    override fun onDrawInFBO(textureId: Int) {

        // 先将textureId的图像画到这一个FBO中
        //激活纹理单元0
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
        //将textureId纹理绑定到纹理单元0
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
        //将纹理单元0传给vTexture,告诉vTexture采样器从纹理单元0读取数据
        GLES20.glUniform1i(vTexture, 0)
        //在textureId纹理上画出图像
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)

        // 右太阳穴
        val rightTemple = getPos(rightTempleIndex)
        // 左太阳穴
        val leftTemple = getPos(leftTempleIndex)
        // 下巴
        val chin = getPos(chinIndex)

        // 圆的半径
        var leftRadius = sqrt(
            pow(chin[0] - leftTemple[0], 2f) + pow(chin[1] - leftTemple[1], 2f)
        ) / 2f
        GLES20.glUniform1f(leftRadiusLocation, leftRadius)
        var rightRadius = sqrt(
            pow(chin[0] - rightTemple[0], 2f) + pow(chin[1] - rightTemple[1], 2f)
        ) / 2f
        GLES20.glUniform1f(rightRadiusLocation, rightRadius)

        // 左边鼠标最远的抬起坐标,即左边太阳穴与下巴中点
        val leftMaxUp = floatArrayOf(
            (leftTemple[0] + chin[0]) / 2f,
            (leftTemple[1] + chin[1]) / 2f
        )
        // 右边鼠标最远的抬起坐标,即右边太阳穴与下巴中点
        val rightMaxUp = floatArrayOf(
            (rightTemple[0] + chin[0]) / 2f,
            (rightTemple[1] + chin[1]) / 2f
        )

        // 左边鼠标点下坐标,为左边太阳穴 绕着 左边最远的抬起坐标,逆时针旋转 90
        val leftDown = floatArrayOf(
            leftMaxUp[0] - (leftTemple[1] - leftMaxUp[1]) * sin(90f),
            leftMaxUp[1] + (leftTemple[0] - leftMaxUp[0]) * sin(90f)
        )
        GLES20.glUniform2f(leftOriginLocation, leftDown[0], leftDown[1])
        // 右边鼠标点下坐标,为下巴 绕着 右边最远的抬起坐标,逆时针旋转 90
        val rightDown = floatArrayOf(
            rightMaxUp[0] - (chin[1] - rightMaxUp[1]) * sin(90f),
            rightMaxUp[1] + (chin[0] - rightMaxUp[0]) * sin(90f)
        )
        GLES20.glUniform2f(rightOriginLocation, rightDown[0], rightDown[1])

        // 以右边点下坐标为坐标系,y轴和右边鼠标最远的抬起坐标的夹角
        val rightTheta = atan(abs(rightDown[0] - rightMaxUp[0]) / abs(rightDown[1] - rightMaxUp[1]))
        // 以左边点下坐标为坐标系,y轴和左边鼠标最远的抬起坐标的夹角
        val leftTheta = atan(abs(leftDown[0] - leftMaxUp[0]) / abs(leftDown[1] - leftMaxUp[1]))

        // 左边抬起坐标
        val leftUp = floatArrayOf(
            leftDown[0] + leftRadius * faceLiftRatio * sin(leftTheta),
            leftDown[1] + leftRadius * faceLiftRatio * cos(leftTheta)
        )
        GLES20.glUniform2f(leftTargetLocation, leftUp[0], leftUp[1])

        // 右边抬起坐标
        val rightUp = floatArrayOf(
            rightDown[0] - rightRadius * faceLiftRatio * sin(rightTheta),
            rightDown[1] + rightRadius * faceLiftRatio * cos(rightTheta)
        )
        GLES20.glUniform2f(rightTargetLocation, rightUp[0], rightUp[1])

        //解除绑定
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
    }

    /**
     * 获取纹理坐标系中,index索引对应的位置
     * @param index Int
     * @return FloatArray
     */
    private fun getPos(index: Int): FloatArray {
        val pos =
            floatArrayOf(facePosition[index * 2], facePosition[index * 2 + 1], 0f, 1f)
        val posResult = FloatArray(4)
        // 因为facePosition中的人脸数据是向左侧着的,因此位置信息需要旋转90度
        Matrix.multiplyMV(posResult, 0, matrix, 0, pos, 0)
        // 现在坐标是在归一化坐标系中的值,而OpenGL程序中是在texture2D函数中使用,需要转换为纹理坐标
        posResult[0] = (posResult[0] + 1f) / 2f
        posResult[1] = (posResult[1] + 1f) / 2f
        return posResult
    }

    /**
     * 更新人脸顶点位置
     * @param facePosition FloatArray
     */
    fun setFacePosition(facePosition: FloatArray) {
        this.facePosition = facePosition
    }

    /**
     * 设置瘦脸程度
     * @param ratio Float
     */
    fun setFaceLiftRatio(ratio: Float) {
        // 因为faceLiftRatio过大,会变得很畸形,因此在这里控制faceLiftRatio在[0,1/4]之间
        faceLiftRatio = ratio / 4f
    }

    /**
     * 设置变换矩阵,否则人脸位置是旋转90的
     * @param matrix FloatArray
     */
    fun setUniforms(matrix: FloatArray) {
        this.matrix = matrix
    }
}

相关文章

  • 廋脸针有危险吗?

    廋脸针有危险吗?瘦脸针的危险在正常的情况下是不存在的,瘦脸针注射后反应较为明显的患者会有局部的肿胀以及发热表现,注...

  • 廋廋的

    人生半世已经到头了 到头来,竟然真的一无所有… 别人吃我喝我窃我 抑或也是稀罕我。 穷孑,却又是一种注定的 人,总...

  • 别看我廋 有点肌肉 ...

  • 为政篇第十:知人,要全方位地了解

    原文 子曰:“视其所以,观其所由,察其所安,人焉廋哉?人焉廋哉?” 孔子,非常强调知人,他曾说,“不患人之不己知...

  • 学习《论语》之感十四

    子曰:“视其所以,观其所由,察其所安,人焉廋哉?人焉廋哉?” 在《之感九》中,子曰:“不患人之不己知,患不知人也。...

  • ps瘦脸教程:ps怎样瘦脸不变形

    欢迎来到易夏岚UI设计之Photoshop系列教学课程,今天我们来学习ps瘦脸教程。有人会说,现在美颜相机这么强大...

  • 廋身

    我还是想用瘦身这个词,因为减肥往往是充满着正能量的词儿。 公司里的几位年轻的女士,从饮食上都特别的注意,减肥是挂在...

  • 瘦脸针人人都能打吗?

    瘦脸针,很多人都利用瘦脸针来瘦脸求美,似乎都是一件很常见的事情了。下面对廋脸针做相关的介绍,一起去了解一下吧。 1...

  • 和瘦子待在一起后果很严重

    当你廋的时候,我也廋 当你胖的时候,我仍然廋 当你胖成猪的时候,我还是廋 是你胖的太快,还是我跟不上你胖的节奏 我...

  • 读《论语》第二十六章 人焉廋哉

    子曰:“视其所以,观其所由,察其所安。人焉廋哉?人焉廋哉?” 廋:隐藏、隐匿。 这则论语,是孔子...

网友评论

      本文标题:相机之廋脸

      本文链接:https://www.haomeiwen.com/subject/silccrtx.html