OpenGL-贴纸方案

news2025/6/22 20:45:13

OpenGL-贴纸方案

普通贴纸(缩放、Z轴旋转、平移)

OpenGL环境说明

OpenGL渲染区域使用正交投影换算,正常OpenGL坐标是vertexData,这样的 Matrix.orthoM
进行换算

    //顶点坐标(原点为显示区域中心店)
    private final float[] vertexData  = {
                -1.0f, -1.0f,  //左下角
                1.0f, -1.0f,   //右下角
                -1.0f,  1.0f,  //左上角
                1.0f,  1.0f,   //右上角
    };
m_width=720;
m_height=1280
 Matrix.orthoM(matrix, 0, 0, m_width, m_height,0 , -1, 1);

    //坐标原点对应屏幕左上角
    private final float[] m_position = {
            0f, 0f,//左上角坐标
            720f,0f,///右上角坐标
            0f, 1280f, //左下角坐标
            720f, 1280f,//右下角坐标
    };

以上面的基础的渲染区域设置FBO的绘制区,进行贴纸绘制,对应的缩放、旋转、平移时候对矩阵的处理和顶点点位处理的方法说明m_position对应的是贴纸的坐标,m_materialRect对应的xy轴的坐标和宽高。


    float[] m_materialRect={0f,0f,500f,500f};//x,y,w,h
    //坐标原点对应屏幕左上角
    private final float[] m_position = {
            0f, 0f,//左上角坐标
            500f, 0f,//右上角坐标
            0f, 500f, //左下角坐标
            500f, 500f,//右下角坐标
    };

void processCommonRect() {
    if (m_locateType == Locate_Common_Rect && m_position) {
        float x = m_materialRect.origin.x * m_width;
        float y = m_materialRect.origin.y * m_height;
        Rect rect(x, y, m_materialRect.size.width, m_materialRect.size.height);
        m_matrix->identity();
        if (m_scale != 1.0f || (m_angle != 0.f && m_angle != 360.f)) {
            m_matrix->scale(m_scale);
            m_matrix->rotateZ(m_angle);
            // 纹理在屏幕上旋转,x 和 y 需要计算屏幕的比例,防止拉伸
            float screenRadio = m_height / m_width;
            float screenRadioFlip = 1.0f / screenRadio;
            float *m = (float *)m_matrix->get();
            m[0] *= screenRadio;
            m[4] *= screenRadio;
            m_matrix->scale(screenRadioFlip, 1, 1);
            if (m_offsetRect.size.width > 0 && m_offsetRect.size.height > 0) {
                float offsetX = m_offsetRect.origin.x - (0.5f - m_offsetRect.size.width / m_width * 0.5f);
                float offsetY = m_offsetRect.origin.y - (0.5f - m_offsetRect.size.height / m_height * 0.5f);
                rect.origin.x -= offsetX * m_width;
                rect.origin.y -= offsetY * m_height;
                m[12] = offsetX * 2.0f;
                m[13] = offsetY * 2.0f;
            } else {
                float centerX = (m_width - rect.size.width) * 0.5f;
                float centerY = (m_height - rect.size.height) * 0.5f;
                m[12] = (rect.origin.x - centerX) / m_width * 2.0f;
                m[13] = (rect.origin.y - centerY) / m_height * 2.0f;
                rect.origin.x = centerX;
                rect.origin.y = centerY;
            }
        }
        if(m_isCut)
        {
            if(m_originalRect.size.width != 0 && m_originalRect.size.height != 0){
                m_texcoord[0].set(0, 0);
                if(1.0f*m_materialRect.size.width/m_originalRect.size.width < 1.0)
                {
                    m_texcoord[1].set(1.0f*m_materialRect.size.width/m_originalRect.size.width, 0);
                }else{
                    m_texcoord[1].set(1.0,0);
                }
                m_texcoord[2].set(0, 1);
                if(1.0f*m_materialRect.size.width/m_originalRect.size.width < 1.0){
                    m_texcoord[3].set(1.0f*m_materialRect.size.width/m_originalRect.size.width, 1);
                }else{
                    m_texcoord[3].set(1.0, 1);
                }
            }else{
                m_texcoord[0].set(0, 0);
                m_texcoord[1].set(1, 0);
                m_texcoord[2].set(0, 1);
                m_texcoord[3].set(1, 1);
                }
            }

        m_position[0].set(rect.origin.x, rect.origin.y);
        m_position[1].set(rect.right(), rect.origin.y);
        m_position[2].set(rect.origin.x, rect.bottom());
        m_position[3].set(rect.right(), rect.bottom());
    }
}


代码解说:

        float x = m_materialRect.origin.x * m_width;
        float y = m_materialRect.origin.y * m_height;
        Rect rect(x, y, m_materialRect.size.width, m_materialRect.size.height);

m_materialRect.origin.xm_materialRect.origin.y分别是占m_width和m_height的比例,相乘就获取真实的x,y坐标值,然后保存到Rect。
m_matrix->identity();是矩阵的初始化, m_matrix->scale(m_scale);进行缩放, m_matrix->rotateZ(m_angle); 进行旋转.

            // 纹理在屏幕上旋转,x 和 y 需要计算屏幕的比例,防止拉伸
            float screenRadio = m_height / m_width;
            float screenRadioFlip = 1.0f / screenRadio;
            float *m = (float *)m_matrix->get();
            m[0] *= screenRadio;
            m[4] *= screenRadio;
            m_matrix->scale(screenRadioFlip, 1, 1);

上面的代码是对进行缩放后的再按屏幕(720,1280)比例进行再次矫正,防止拉伸,实际上的操作 m[0] *= screenRadio*screenRadioFlip; m[4] *= screenRadio*screenRadioFlip; 如果不理解可以查看缩放矩阵和Z轴旋转矩阵相乘

                float centerX = (m_width - rect.size.width) * 0.5f;
                float centerY = (m_height - rect.size.height) * 0.5f;
                m[12] = (rect.origin.x - centerX) / m_width * 2.0f;
                m[13] = (rect.origin.y - centerY) / m_height * 2.0f;
                rect.origin.x = centerX;
                rect.origin.y = centerY;
  1. float centerX = (m_width - rect.size.width) * 0.5f;:计算屏幕宽度减去矩形宽度后的一半,以此确定矩形在 x 轴上居中的位置。

  2. float centerY = (m_height - rect.size.height) * 0.5f;:计算屏幕高度减去矩形高度后的一半,以此确定矩形在 y 轴上居中的位置。

  3. m[12] = (rect.origin.x - centerX) / m_width * 2.0f;:这行代码的目的是根据矩形左上角的 x 坐标相对于屏幕中心点的偏移量来计算 x 轴的位移量。以下是具体步骤:

    • (rect.origin.x - centerX):计算矩形左上角 x 坐标和屏幕中心 x 坐标之间的偏移量。
    • / m_width:将得到的偏移量除以屏幕宽度,将其转换为比例,结果范围在 [-0.5, 0.5] 之间。
    • * 2.0f:最后乘以 2.0,将比例倍增,确保偏移量适合矩阵的变换范围。而归一化后的[-0.5, 0.5]范围不太适合用于这些变换。通过乘以2,可以使得矩阵的位移调整更加明显和精确[-0.1, 0.1]
  4. 这样计算出的结果将会被存储在矩阵 m_matrix 的第 12 个元素中,通常表示 x 轴的位移信息。

总的来说,这段代码用于根据矩形左上角与屏幕中心的偏移量,计算并设置矩阵的 x 方向位移,以便将矩形移动到屏幕的中心位置,从而实现在屏幕中心进行正确显示和定位。

Matrix4说明:

Matrix4& Matrix4::identity()
{
    m[0] = m[5] = m[10] = m[15] = 1.0f;
    m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0.0f;
    return *this;
}


Matrix4& Matrix4::rotateZ(float angle)
{
    angle *= DEG2RAD;
    float c = cosf(angle);
    float s = sinf(angle);
    float m0 = m[0], m1 = m[1], m2 = m[2],  m3 = m[3],
    m4 = m[4], m5 = m[5], m6 = m[6],  m7 = m[7];
    
    m[0] = m0 * c + m4 *-s;
    m[1] = m1 * c + m5 *-s;
    m[2] = m2 * c + m6 *-s;
    m[3] = m3 * c + m7 *-s;
    m[4] = m0 * s + m4 * c;
    m[5] = m1 * s + m5 * c;
    m[6] = m2 * s + m6 * c;
    m[7] = m3 * s + m7 * c;
    
    return *this;
}


Matrix4& Matrix4::scale(float s)
{
    return scale(s, s, s);
}

Matrix4& Matrix4::scale(float x, float y, float z)
{
    m[0] = m[0]*x;   m[1] = m[1]*x;   m[2] = m[2]*x;   m[3] = m[3]*x;
    m[4] = m[4]*y;   m[5] = m[5]*y;   m[6] = m[6]*y;   m[7] = m[7]*y;
    m[8] = m[8]*z;   m[9] = m[9]*z;   m[10]= m[10]*z;  m[11]= m[11]*z;
    return *this;
}

缩放转的矩阵等于:
在这里插入图片描述
沿Z轴旋转的矩阵等于:
在这里插入图片描述

计算重心坐标原理

已知三角形3顶点坐标A(x1,y1),B(x2,y2),C(x3,y3),求三角形ABC的面积的公式

写成一般形式如下:
设A(x1,y1),B(x2,y2),C(x3,y3)在坐标系中中顺序为三点按逆时针排列,对应的权重 ( weight1, weight2, weight3 )

weight1 + weight2 + weight3 = 1

S=1/2[(x1y2-x2y1)+(x2y3-x3y2)+(x3y1-x1y3)]

//分别计算三个点对总面积的贡献 这一步表示每个点在总面积中所占的比例。
S1 = S * weight1;
S2 = S * weight2;
S3 = S * weight3;
//计算加权平均的重心坐标 (xw, yw)
 xw = (S1 * x1 + S2 * x2 + S3 * x3) / S;
 yw = (S1 * y1 + S2 * y2 + S3 * y3) / S;

在这里插入图片描述

在重心坐标系中,三角形平面的任何一个点(x,y)都可以表示成三角形三个顶点的线性组合(系数分别是α β γ \alpha \beta \gammaαβγ,且满足这三个系数相加和为1)
在这里插入图片描述
获得三角形任意一点的重心坐标
在这里插入图片描述

人脸贴纸说明

1=weight1+weight2+weight3
p_index_1=44
weight1=-2.2564
p_index_2=38
weight2=1.6250
p_index_3=37
weight3=1.6314


void processFace2DLocate()
{
    if (m_position == nullptr) {
        Rect rect = m_materialRect;
        rect.origin.x = (m_width - rect.size.width) * 0.5f;
        rect.origin.y = (m_height - rect.size.height) * 0.5f;
        
        // 更新数据
        int count = 4;
        m_position = new Vector2[count];
        m_position[0].set(rect.origin.x, rect.origin.y);
        m_position[1].set(rect.right(), rect.origin.y);
        m_position[2].set(rect.origin.x, rect.bottom());
        m_position[3].set(rect.right(), rect.bottom());
        m_texcoord = new Vector2[count];
        m_texcoord[0].set(0, 0);
        m_texcoord[1].set(1, 0);
        m_texcoord[2].set(0, 1);
        m_texcoord[3].set(1, 1);
        m_indexCount = 6;
        m_pointIndex = new unsigned short[m_indexCount]{0, 1, 2, 2, 1, 3};
        
        m_matrix = new Matrix4;
    }
    
    m_matrix->identity();
    Vector2* point106 = m_face->getPoint106();

    float screenRadio = m_height / m_width;
    float screenRadioFlip = 1.0f / screenRadio;

    float x_dis = std::fabs(point106[32].x - point106[0].x) * screenRadioFlip;
    float y_dis = std::fabs(point106[32].y - point106[0].y);
    // 标准人脸0\32之间的距离
    float standard_face_x = 475.0f / m_width;
    float standard_face_y = 0.0f;
    float scale = std::sqrt(x_dis * x_dis + y_dis * y_dis) / std::sqrt(standard_face_x * standard_face_x + standard_face_y * standard_face_y) * screenRadio;
    
    int index1 = m_2DLocateParam.p_index_1;
    float weight1 = m_2DLocateParam.p_weight_1;
    int index2 = m_2DLocateParam.p_index_2;
    float weight2 = m_2DLocateParam.p_weight_2;
    int index3 = m_2DLocateParam.p_index_3;
    float weight3 = m_2DLocateParam.p_weight_3;
    
    float _x1 = point106[index1].x;
    float _y1 = point106[index1].y;
    float _x2 = point106[index2].x;
    float _y2 = point106[index2].y;
    float _x3 = point106[index3].x;
    float _y3 = point106[index3].y;
    
    // 总面积
    float S = 0.5 * fabs((_x1 * _y2 - _x2 * _y1) + (_x2 * _y3 - _x3 * _y2) + (_x3 * _y1 - _x1 * _y3));
    
    // s_index1
    float S1 = S * weight1;
    // s_index2
    float S2 = S * weight2;
    // s_index3
    float S3 = S * weight3;

    float xw = (S1 * _x1 + S2 * _x2 + S3 * _x3) / S;
    float yw = (S1 * _y1 + S2 * _y2 + S3 * _y3) / S;

    m_matrix->scale(scale);
    
    m_matrix->rotate(m_face->getYaw() * 1.2, 0.0, 1.0, 0.0);
    m_matrix->rotate(m_face->getPitch() * 1.2, 1.0, 0.0, 0.0);
    m_matrix->rotate(m_face->getRoll(), 0.0, 0.0, 1.0);
//    printf("m_face->getRoll() = %f, %f, %f\n", 360 + m_face->getRoll(), m_face->getPitch(), m_face->getYaw());

    // 纹理在屏幕上旋转,x 和 y 需要计算屏幕的比例,防止拉伸
    float *m = (float *)m_matrix->get();
    m[0] *= screenRadio;
    m[4] *= screenRadio;
    m_matrix->scale(screenRadioFlip, 1, 1);
    
    
    m[12] = xw * 2.f - 1.f;
    m[13] = yw * 2.f - 1.f;
}

m_matrix 进行旋转变换,根据人脸的偏航角(yaw)、俯仰角(pitch)和横摆角(roll)来调整姿态。让我给您解释一下:

  1. m_matrix->rotate(m_face->getYaw() * 1.2, 0.0, 1.0, 0.0);

    • 这行代码根据人脸的偏航角(yaw)来进行绕 Y 轴旋转。
    • m_face->getYaw() 是获取人脸的偏航角度,乘以1.2用于增加旋转幅度。
    • 参数 (0.0, 1.0, 0.0) 表示围绕 Y 轴旋转。
  2. m_matrix->rotate(m_face->getPitch() * 1.2, 1.0, 0.0, 0.0);

    • 这行代码根据人脸的俯仰角(pitch)来进行绕 X 轴旋转。
    • m_face->getPitch() 是获取人脸的俯仰角度,乘以1.2用于增加旋转幅度。
    • 参数 (1.0, 0.0, 0.0) 表示围绕 X 轴旋转。
  3. m_matrix->rotate(m_face->getRoll(), 0.0, 0.0, 1.0);

    • 这行代码根据人脸的横摆角(roll)来进行绕 Z 轴旋转。
    • m_face->getRoll() 是获取人脸的横摆角度。
    • 参数 (0.0, 0.0, 1.0) 表示围绕 Z 轴旋转。

在这里插入图片描述

point106[32]和point106[0]分别是左右两边脸部两个点,下面代码为了计算脸部对屏幕上的缩放比例,std::sqrt(x_dis * x_dis + y_dis * y_dis) 计算了特征点索引为 0 和 32 之间的欧几里德距离。同时,std::sqrt(standard_face_x * standard_face_x + standard_face_y * standard_face_y) 则计算了标准人脸上 x 和 y 方向的长度与屏幕尺寸的比例之间的欧几里德距离。

 float x_dis = std::fabs(point106[32].x - point106[0].x) * screenRadioFlip;
    float y_dis = std::fabs(point106[32].y - point106[0].y);
    // 标准人脸0\32之间的距离
    float standard_face_x = 475.0f / m_width;
    float standard_face_y = 0.0f;
    float scale = std::sqrt(x_dis * x_dis + y_dis * y_dis) / std::sqrt(standard_face_x * standard_face_x + standard_face_y * standard_face_y) * screenRadio;

重心坐标值(xw,yw)在[0, 1]范围转化为[-1, 1] 范围,这个顶点坐标标准

   m[12] = xw * 2.f - 1.f;
   m[13] = yw * 2.f - 1.f;

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

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

相关文章

小米公司研发岗的年终奖。。

小米 好的公司有年终且在年前发放&#xff0c;一般的公司有&#xff08;可能打折的&#xff09;年终且年后分批发放&#xff0c;不好的公司各有操作。 3 月已来&#xff0c;小米的年终也开始热议起来。 最近&#xff0c;一则「传小米年终打折&#xff0c;14薪能保住吗」冲上热搜…

electron + vtkjs加载模型异常,界面显示类似于图片加载失败的图标

electron vtkjs加载模型显示异常&#xff0c;类似于图片加载失败的效果&#xff0c;如上图。 electron开发版本&#xff1a;13。 解决方法&#xff1a;升级electron版本号。 注意&#xff1a;win7最高兼容electron 22版本。

华为机考:HJ3 明明的随机数

华为机考&#xff1a;HJ3 明明的随机数 描述 代码 set&#xff0c;插入即排序&#xff0c;而且没有重复数字 #include<iostream> #include<vector> #include<algorithm> using namespace std;int main(){int n;while(cin >> n){ //首先输入每次调查…

Rabbit算法:轻量高效的加密利器

title: Rabbit算法&#xff1a;轻量高效的加密利器 date: 2024/3/13 18:14:31 updated: 2024/3/13 18:14:31 tags: Rabbit算法流密码高安全性高性能密钥调度加密解密抗攻击性 Rabbit算法起源&#xff1a; Rabbit算法是由Martin Boesgaard和Mette Vesterager提出的一种流密码算…

1960-2020年全球双边迁移数据库(Global Bilateral MigrationDatabase)

1960-2020年全球双边迁移数据库&#xff08;Global Bilateral MigrationDatabase&#xff09; 1、时间&#xff1a;1960-2000年&#xff0c;每10年一次具体为&#xff1a;1960年、1970年、1980年、1990年、2000年 2、来源&#xff1a;世界银行 3、指标&#xff1a;Country O…

这款自动引流软件居然能让你的营销效果翻倍提升!

在数字化时代&#xff0c;营销策略的高效执行对企业来说至关重要。自动引流软件作为现代企业营销工具箱中的一员&#xff0c;其重要性不言而喻。这类软件通过智能化、自动化的方式&#xff0c;将潜在客户吸引到企业的销售渠道中&#xff0c;从而为企业带来可观的收益和品牌曝光…

【算法】一类支持向量机OC-SVM(1)

【算法】一类支持向量机OC-SVM 前言一类支持向量机OC-SVM 概念介绍示例编写数据集创建实现一类支持向量机OC-SVM完整的示例输出 前言 由于之前毕设期间主要的工具就是支持向量机&#xff0c;从基础的回归和分类到后来的优化&#xff0c;在接触到支持向量机还有一类支持向量机的…

能发顶会!GNN结合LLMs的三大创新思路!新SOTA准确率提升10倍

LLMs在处理NLP任务方面表现出色&#xff0c;而GNNs在挖掘和分析复杂关系数据&#xff08;图数据&#xff09;方面展现出其卓越的能力。这种趋势催生了将这两种技术整合的研究兴趣&#xff0c;为解决更多领域的实际问题。GNN结合LLMs也逐渐成为了研究的热点。 GNNLLMs可以发挥二…

使用Docker管理linux容器

文章目录 一、使用docker管理镜像 二、使用docker管理容器 一、使用docker管理镜像 1、安装操作系统&#xff0c;我安装的是centOS 7 &#xff0c;因为centos7有着非常丰富的软件仓库&#xff0c;方便后续安装与docker相关的软件。 2、初始化设置&#xff0c; 关闭防火墙 关闭…

有来团队后台项目-解析7

sass 安装 因为在使用vite 创建项目的时候,已经安装了sass,所以不需要安装。 如果要安装,那么就执行 npm i -D sass 创建文件 src 目录下创建文件 目录结构如图所示: reset.scss *, ::before, ::after {box-sizing: border-box;border-color: currentcolor;border-st…

未来已来:科技驱动的教育变革

我们的基础教育数百年来一成不变。学生们齐聚在一个物理空间&#xff0c;听老师现场授课。每节课时长和节奏几乎一致&#xff0c;严格按照课表进行。老师就像“讲台上的圣人”。这种模式千篇一律&#xff0c;并不适用于所有人。学生遇到不懂的问题&#xff0c;只能自己摸索或者…

案例LocalDate ,Map集合,split方法分割,集合、Random随机数、多态、封装

case1&#xff1a; 集合、Random随机数、多态、封装 ShootOddPeople类 package com.zz.examine.case1;import java.util.ArrayList; import java.util.List; import java.util.Random;/****/ public class ShootOddPerson {public static List<People> peoples new Ar…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的零售柜商品检测软件(Python+PySide6界面+训练代码)

摘要&#xff1a;开发高效的零售柜商品识别系统对于智能零售领域的进步至关重要。本文深入介绍了如何运用深度学习技术开发此类系统&#xff0c;并分享了全套实现代码。系统采用了领先的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5进行了性能比较&#xff0c;呈现了诸如…

CTP-API开发系列之九:行情登录及订阅代码

CTP-API开发系列之九&#xff1a;行情登录及订阅代码 前情回顾全局配置参数行情初始化代码行情登录行情订阅行情接收注意事项 前情回顾 CTP-API开发系列之一&#xff1a;各版本更新说明&#xff08;持续更新&#xff09; CTP-API开发系列之二&#xff1a;问题汇总&#xff08;…

ONLYOFFICE 文档开发者版,为您的平台带来强大的文档编辑功能

你是否在寻找一个可自主部署、可定制、易集成的文档编辑器解决方案&#xff1f;如果是这样&#xff0c;那么ONLYOFFICE 文档开发者版&#xff0c;也许就是你想要的答案。下面让我们一起来看看它有哪些特点&#xff0c;并能为您带来哪些好处。 什么是 ONLYOFFICE 文档 ONLYOFFI…

06-kafka及异步通知文章上下架-黑马头条

kafka及异步通知文章上下架 1)自媒体文章上下架 需求分析 2)kafka概述 消息中间件对比 特性ActiveMQRabbitMQRocketMQKafka开发语言javaerlangjavascala单机吞吐量万级万级10万级100万级时效性msusmsms级以内可用性高&#xff08;主从&#xff09;高&#xff08;主从&#…

OpenMP 编程模型

OpenMP 内存模型 共享内存模型&#xff1a; OpenMP 专为多处理器/核心、共享内存机器设计&#xff0c;底层架构可以是共享内存UMA或NUM OpenMP 执行模型 基于线程的并行&#xff1a; OpenMP 程序基于多线程来实现并行&#xff0c; 线程是操作系统可以调度的最小执行单元。 …

【C++】string进一步介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 迭代器2.1 反向迭代器2.2 const对象迭代器 3. Capacity3.1 size和length3.2 max_size3.3 capacity3.4 clear3.5 shrink_to_fit &#xff08;了解即可&#xff09;3.6 reserve3.7 resize 4. Element access4…

Orange3数据预处理(离散化组件)

离散化&#xff1a;将数值属性转换为分类属性。 输出 数据&#xff1a;具有离散化值的数据集 设置离散化的默认方法。 选择变量以为每个变量设置特定的离散化方法。将鼠标悬停在变量上显示区间。 离散化方法Keep numeric(保持数值)&#xff1a;保持变量不变。Remove (移除)&a…

【强化学习抓取】偏机器人领域(略)

文章目录 1. A Grasp Pose is All You Need: Learning Multi-fingered Grasping with Deep Reinforcement Learning from Vision and Touch摘要和结论引言相关工作模型框架 1. A Grasp Pose is All You Need: Learning Multi-fingered Grasping with Deep Reinforcement Learni…