自己写Shader-翻书效果

思路

  1. 直直的翻页,即将页面旋转180完成翻页,不存在弯曲效果
  2. 弯曲翻页,页面从一个角有曲线的翻动

翻页原理

https://blog.csdn.net/ls4985/article/details/110195030
https://gitbook.cn/books/5f3bd55ab74e2f16f57ea9e6/index.html

直翻页:

https://zhuanlan.zhihu.com/p/28836892
直翻页参考上边,但是他的效果不好(谁见过书从中间翻出来一页的。。),所以自己使用unity默认plane来重新弄了一个,将plane分为两半,使用一半来贴纹理,然后翻页到另一半。

思路:

  1. 书本效果分析:
    如图,将书分为三种页面:
    在这里插入图片描述
    第一种:黄色的竖起来的那一页,正反两个面
    第二种:想象自己在桌子下面,往天上看到的两个页面,即图中紧贴地面的两个面
    第三种:剩下的两个面,即 图中看到的左右两个灰色页面

  2. shader设计:
    对三种页面进行分析:
    其中两个页面可以公用顶点着色器,一个页面需要顶点翻转效果
    我这里分出了四个pass:
    第一个 渲染正面 左半页不动,采样 右半页翻动,采样
    第二个 渲染背面 左半页不动,不采样(多余) 右半页翻动,采样
    第三个 渲染正面 左半页不动,不采样(多余) 右半页不动,采样
    第四个 渲染背面 左半页不动,采样 右半页不动,采样
    渲染了8个页面,两个不需要,进行透明,这样6个页面就都渲染出来
    注意在翻书的 开始和结尾 透明控制

  3. 纹理设计:
    对渲染的贴图分析:
    将一本书的图片分成左右两部分图片,封面为右侧第一张,封底为左侧最后一张

    Texture bookfirst;
    Texture booklast;
    Texture[] leftPages;
    Texture[] rightPages;
    之所以记录封面和封底是因为书不管什么状态这两个图片总是要渲染出来。

  4. 翻页逻辑:
    剔除第一页和最后一页的特殊情况。
    书本会同时出现左侧两幅图,右侧两幅图 +封面和封底。(6张贴图)
    每翻动一次,贴图往后移动一张。

  5. 其他:
    这里因为书本第一页和书本最后一页翻动时需要透明,所以这两处情况需要特殊处理,再shader中判断。
    针对这种情况,可以对 左右分页的图片 增加透明图片来变为通用情况。即第一页时左侧第一个图片为透明图片,最后一页时,右侧最后图片为透明。 这样在逻辑判断时就不需要多余判断了

  6. 脚本逻辑:
    因为shader需要不断更替左右页的贴图。所以必然要通过脚本来控制
    1.控制贴图在翻页完成时 更替。
    2.由总贴图数量,控制翻页次数。
    3.管理翻页进度。给定进度即可渲染当前书状态。使用进度条管理书本翻动。

代码:

这里贴出了使用透明图片占位的代码。不使用透明图片需要自己再加前后状态的判断即可。

Shader部分:

Shader "SxerShader/BookEffectShader1"
{	/*
		翻页效果,实现页面直翻动180度的效果
		参考:https://zhuanlan.zhihu.com/p/28836892
		制作思路:
		1.使用unity默认plane来实现翻页,且 要分为 左右 页面来翻动,而不是参考中的一个页面从中间翻动
		2.为了将plane分为左右部分,需要对原plane顶点uv重新划分(以uv横坐标的一半来划分)
			右侧:uv横坐标大于0.5的部分,即【0.5,1】映射为【0,1】  (向左移0.5再扩大2即可)
			左侧:【0,0.5】映射为【0,1】  (扩大2即可)
		3.这样划分的话,左右页面会共用中线的顶点数据,所以分开存储两幅uv坐标
		4.在片元着色器采样时,因为片元取的是多个顶点混合数据,所以会产生一侧的纹理对另一侧影响【即在中线左右两排的顶点之间存在颜色过渡】
		5.为了剔除这种影响,增加左右顶点标识变量LR,值为1则说明该点位于哪一侧
		6.但 若是直接将标识为0的颜色透明,则会有花屏现象,应该还是顶点混合的缘故,所以以<0.0001来代替

	*/
	//把书平铺打开,然后翻起一页(倒T型)
	//可以划分出三个正面,三个背面
	//紧贴桌面的两个面(背面),和两个面的反面(正面)  翻动的页面的正反两面
	
	//当书是这个状态时,渲染了6副图,  书封面和封底,左侧的一页,翻起的左侧第二页,翻起的右侧一页,平铺的右侧第二页
	
	//把书页划分为左右
	//右一即为封面
	//左最后为封底
	//且在翻动过程中  存在特殊情况:即刚开始 左一为透明(书本未打开时)
	//							 结束时 右最后为透明(最后一页翻过来时)
	//	不考虑特殊情况时  每翻动一次书页 左右图片进度+1
	 

	//左面 右面
	Properties{
		//封面
		_BookFirst("BookFirst Page",2D) = "white"{}
		_BookLast("BookLast Page",2D) = "white"{}
		//右1 右2 左1 左2
		_RightPageTex("RightPage Texture",2D)= "white"{}
		_RightPage2Tex("RightPage 2 Texture",2D) = "white"{}
		_LeftPageTex("LeftPage Texture",2D)= "white"{}
		_LeftPage2Tex("LeftPage 2 Texture",2D)="white"{}
		
		_PlaneXLength("PlaneModel X Length",float)=1

		_BookState("Book State",int)=2

	//	_MainTex("Main Texture",2D)="white"{}
		_DiffuseColor("Diffuse Color",Color)=(1,1,1,0)
		_PageAngle("Page Angle",Range(0,1)) = 0
	}
	SubShader{
			Tags{
				"RenderType" = "Transparent"
				"Queue" = "Transparent"
				"IgnoreProjector" = "True"
			}
			CGINCLUDE
			#include "UnityCG.cginc"
			sampler2D _RightPageTex;
			sampler2D _RightPage2Tex;
			sampler2D _LeftPageTex;
			sampler2D _LeftPage2Tex;
			sampler2D _BookFirst;
			sampler2D _BookLast;

			fixed4 _DiffuseColor;
			float _PageAngle;
			float _PlaneXLength;
			int _BookState;
			#define PI 3.1415926

			float4 moveVertexByAngle(float4 ver){
				float an = PI * _PageAngle;
				float4 tempVertex = ver;
				tempVertex.x = ver.x * cos(an);
				tempVertex.y = ver.x * sin(an);
				return tempVertex;
			}

			struct v2f{
				float4 pos:SV_POSITION;
				float4 uv:TEXCOORD0;//xy存储右侧,zw存储左侧
				float2 LR:TEXCOORD1;//区分左右页的顶点
			};

			//顶点翻转效果
			v2f vert_flip(appdata_base v){
				v2f o;
				float4 vPos;
				o.uv.xy = o.uv.zw = 1 - v.texcoord;//plane颠倒
				//unity默认plane的x>0部分为书的右半部分 右侧的顶点uv变动 贴一张纹理
				if(o.uv.x >= 0.5){
					o.uv.x = (o.uv.x - 0.5) * 2;//右侧一半为一页
					o.LR = float2(0,1);//右侧标记
					vPos = moveVertexByAngle(v.vertex);//右侧旋转
				}
				//左边
				if(o.uv.z <= 0.5){
					o.uv.z *= 2;//默认uv原点即在左边,且plane左右对称,所以左侧u缩小一半即可
					o.LR = float2(1,0);//左侧
					vPos = v.vertex;//左侧不动
				}
				//左右分页存在公用顶点(中线)。且顶点的参数值在片元着色时会产生影响。这里单独设置
				if(v.vertex.x == 0){
					//纹理采样时 两个顶点间会有颜色过渡   左右页面会互相影响   0.5影响小点
					o.LR = float2(0,0);
				}
				o.pos = UnityObjectToClipPos(vPos);
				return o;
			}

			//仅改变uv
			v2f vert(appdata_base v){
				v2f o;
				float4 vPos;
				vPos = v.vertex;
				o.uv.xy = o.uv.zw = 1 - v.texcoord;
				//unity默认plane的x>0部分为书的右半部分 右侧的顶点uv变动 贴一张纹理
				if(o.uv.x >= 0.5){
					o.uv.x = (o.uv.x - 0.5) * 2;//右侧一半为一页
					o.LR = float2(0,1);//右侧标记
				}
				//左边
				if(o.uv.z <= 0.5){
					o.uv.z *= 2;//默认uv原点即在左边,且plane左右对称,所以左侧u缩小一半即可
					o.LR = float2(1,0);//左侧
				}
				//左右分页存在公用顶点(中线)。且顶点的参数值在片元着色时会产生影响。这里单独设置
				if(v.vertex.x == 0){
					//纹理采样时 两个顶点间会有颜色过渡   左右页面会互相影响   0.5影响小点
					o.LR = float2(0,0);
				}
				o.pos = UnityObjectToClipPos(vPos);
				return o;
			}
			 
			//底部的左右页面
			fixed4 frag_back_both(v2f i):SV_Target{
				fixed4 rightColor;
				fixed4 leftColor;
				fixed4 mainColor;

				//根据视觉 翻转
				i.uv.x = 1- i.uv.x;
				i.uv.z = 1- i.uv.z;
				//右页纹理采样
				//背部封面
				if(_BookState<0){
					rightColor.rgba = fixed4(1,1,1,0);
				}else{
					rightColor.rgba = tex2D(_BookLast,i.uv.xy).rgba;
				}		
				//片元着色器差值影响  //片元的值受周围顶点影响,所以不做变动的话,左右页面交界处会相互影响。
				//在顶点中,以中线为0,中线两侧逐渐增大,所以,这里的值越接近零,两个页面越接近,
				//对右侧页面来说,左边透明
				if(i.LR.y<0.00001)
					rightColor.a = 0;

				//左页纹理采样
				//封面
				if(_BookState>0){
					leftColor.rgba = fixed4(1,1,1,0);
				}else{
					leftColor.rgba = tex2D(_BookFirst,i.uv.zw).rgba;
				}
				//对左侧来说,右侧透明
				if(i.LR.x<0.00001)
					leftColor.a = 0;
				
				//左页纹理采样
				
				//应用两侧的透明
				mainColor = leftColor * leftColor.a + rightColor * rightColor.a;
				return mainColor;
			}


			fixed4 frag_both(v2f i):SV_Target{
				fixed4 rightColor;
				fixed4 leftColor;
				fixed4 mainColor;
				//右一纹理采样
				rightColor.rgba = tex2D(_RightPageTex,i.uv.xy).rgba;
				if(i.LR.y<0.00001)
					rightColor.a = 0;
				
				//左一纹理采样 //如果用透明图占位 则直接采样左一纹理即可
				leftColor.rgba = tex2D(_LeftPageTex,i.uv.zw).rgba;
				if(i.LR.x<0.00001)
					leftColor.a = 0;
				//应用两侧的透明
				mainColor = leftColor * leftColor.a + rightColor * rightColor.a;
				return mainColor;
			}

			//左二纹理采样 
			fixed4 frag_back_half(v2f i):SV_Target{
				fixed4 rightColor;

				i.uv.x = 1 - i.uv.x;//根据视觉背面左右颠倒

				//封面 //如果用透明图占位 则直接采样左二纹理即可
				rightColor.rgba = tex2D(_LeftPage2Tex,i.uv.xy).rgba;
				//右页纹理采样
				
				//片元着色器差值影响  //片元的值受周围顶点影响,所以不做变动的话,左右页面交界处会相互影响。
				//在顶点中,以中线为0,中线两侧逐渐增大,所以,这里的值越接近零,两个页面越接近,
				//对右侧页面来说,左边透明
				if(i.LR.y<0.00001)
					rightColor.a = 0;

				return rightColor;
			}

			//左右采样不反转,采样右侧第二纹理
			fixed4 frag_half_Second(v2f i):SV_Target{
				fixed4 rightColor;
				//背部封面//如果用透明图占位 则直接采样右二纹理即可
				rightColor.rgba = tex2D(_RightPage2Tex,i.uv.xy).rgba;
				if(i.LR.y<0.00001)
					rightColor.a = 0;
				return rightColor;
			}


		ENDCG
		
		//第三个	渲染正面 左半页不动,不采样	右半页不动,采样
		//最后一页正面     左侧 透明   右侧显示第二页     //
		Pass{//这个pass如果放在第一页正面后边,会出现显示bug
			Cull Back
			//Offset 1, 1
			ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_half_Second	
			ENDCG
		
		}

		//第一个 渲染正面	左半页不动,采样	右半页翻动,采样
		Pass{
			Cull Back//剔除背面
			ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			#pragma vertex vert_flip
			#pragma fragment frag_both
			ENDCG
		}

		//第二个 渲染背面	左半页不动,不采样	右半页翻动,采样    
		//采样左侧第一张纹理
		Pass{
			Cull Front//剔除正面
			ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert_flip
			#pragma fragment frag_back_half
			ENDCG
		}
		//最后一页 背面   左侧透明   右侧显示左侧最后一页
		//第四个 渲染背面	左半页不动,采样	右半页不动,采样
		Pass{
			Cull Front
			
			ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_back_both	
			ENDCG
		}

	}
	FallBack Off
}

脚本控制部分:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
 * 思路:
 * 1.一本书的一个页面由左页和右页组成
 * 2.注意左右页在翻动时的变动规则:
 * 因为在翻书的过程中,会出现 右页两个页面同时出现
 * 
 * 可以看作:对于左侧页面来说,第一页放了一张透明图占位
 *          对于右侧页面来说,最后一页放了一张透明图占位
 

 
 */

[ExecuteInEditMode]
public class BookEffect1 : MonoBehaviour
{
    public const int leng = 10;
    [Range(0, leng)]
    public float progress = 0;

    public Material mat;

    public List<Texture> rightPages;
    public List<Texture> leftPages;
   
    /// <summary>
    /// 可翻页数
    /// </summary>
    public int maxPage = 0;
  
    private void Start()
    {
        //因为前后都多透明图片,所以可翻页数-1
        maxPage = rightPages.Count - 1;
       
        //将progress映射到书页进度
        ChangePageImage(progress / (float)leng * (maxPage));
    }

    private void ChangePageImage(float pro)
    {
        int page = (int)pro;//页数

        //翻页角度
        float angle = pro - page;
        mat.SetFloat("_PageAngle", angle);
        
        //页数限制
        page = Mathf.Clamp(page, 0, maxPage);
        
        if (page == 0)//首页
        {
            mat.SetInt("_BookState", 1);
        }
        else if (page == maxPage - 1) //最后一页图片替换
        {
            mat.SetInt("_BookState", -1);
        }
        else if(page == maxPage)//保持最终的看完状态
        {
            mat.SetFloat("_PageAngle", 1);
            return;
        }
        else
        {
            mat.SetInt("_BookState", 0);
        }
        mat.SetTexture("_LeftPageTex", leftPages[page]);
        mat.SetTexture("_LeftPage2Tex", leftPages[page + 1]);
        mat.SetTexture("_RightPageTex", rightPages[page]);
        mat.SetTexture("_RightPage2Tex", rightPages[page + 1]);

        mat.SetTexture("_BookFirst", rightPages[0]);
        mat.SetTexture("_BookLast", leftPages[maxPage - 1]);
    }


    private void Update()
    {
       ChangePageImage(progress / (float)leng * (maxPage));
    }
}

总结:

问题:
这算是自己第一次边思考效果,边写功能,边解决出现的问题实现的shader。
遇到了很多新鲜的问题
1.片元着色器的混合顶点间边界值问题
2.pass顺序前后遮挡效果
3.自己写功能达不到先设计再写,理清楚思路再动手的境界,往往是想一点写一点,写完后再总结修改。
4.遇到特殊问题时,在换方案和解决问题之间需要在多思考。比如这次遇到的边界值混合,一侧的纹理总是会影响另一侧,偶然的情况下判断了0.0001的阈值解决了,原本都准备换模型来适配了。

优化:
可以对直翻页加入参考连接里的曲线效果,看起来更柔和点。
??:
既然三页的书本效果出来了,是不是能做一个 支持多页的翻书?

曲线从一角翻页

先从最简单的效果搞起,如图,说是曲线翻页,先做角翻页

在这里插入图片描述

思路:

效果

在写翻书效果前,先看下这个简单shader

Shader "Unlit/Linear"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Value("Value", Range(0,1))=0
        _Value2("Value2", Range(0,1))=0
    }
    SubShader
    {
        Tags{
				"RenderType" = "Transparent"
				"Queue" = "Transparent"
				"IgnoreProjector" = "True"
		}
        
        Pass
        { 
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _Value;
            float _Value2;

            v2f vert (appdata_base v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                //lerp混合x,y  
                //if(lerp(i.uv.x,i.uv.y,_Value)<_Value)
                //    col *= 0;
                if(lerp(i.uv.x,i.uv.y,_Value)<_Value2)
                    col *= 0;
                //if(distance(i.uv,float2(0.5,0.5))>_Value)
                //    col *= 0;
                return col;
            }
            ENDCG
        }
    }
    FallBack Off
}

调节_value与_value2 可以看到类似书页折叠的效果,两个值一个是翻书角度,一个是翻开大小

对于一个角来说,两个极值点,当_value=0(书页直翻转90) 当_value=1(从另一个方向翻转90) 在0-1之间时存在角的翻页。
注意,当为0时,_value2可以取0-1,但为1时,_value2只能是0【书本只能一个方向翻页,左右翻页的书,你不可能上下翻】
针对这一现象,你会发现在_value变化时,对应的_value2范围是变化的,即
【_value2的取值为(0,1-_value)】 虽然在shader里超过也可以,但是实际翻书时,如果超过了,你就把书页撕掉了!

把效果和数学联系起来

看下边shadertoy第一个效果:
通过自己翻动发现:

  1. 翻折角的位置是以半页书的宽度为半径,以中点为圆心的原内
  2. 当鼠标x不变,y值影响 翻转角度 即上边的_value 且x影响y的最大值
    当鼠标y不变,x值影响 进度 即_value2

角翻页有点复杂,,,

先贴几个shadertoy的翻书效果和代码:

  • 效果不错的:
    翻书
    代码:
#define PI 3.14159265

#define BOOK_BOUNDS vec4(0.15, 0.05, 0.85, 0.95)

#define LIGHT_DIR vec3(5,3,1)
#define SPECULAR_SHININESS 16.0
#define SPECULAR_COLOR vec3(1.0,1.0,1.0)
#define SPECULAR_POWER 10.0

vec3 fakeSpine(vec3 col, float t, float darken)
{
    return mix(col * darken,col , min(1.0,pow(abs(t - 0.5) * 30.0, 0.5)));
}

vec3 fakeNormal(float t, float center)
{
    t -= center;
    float interp =(1.0 - abs((t - 0.5) * 2.0)) * PI + PI * (3.0/4.0) ;
        
    vec3 normal = vec3(abs(sin(interp)) ,0,abs(cos(interp)));

    return mix(normal, vec3(0,0,1), min(1.0,pow(abs(t - 0.5) * 5.0, 0.5)));
}

vec3 specular(vec3 viewDir, vec3 normal )
{
    vec3 lightDir = normalize(LIGHT_DIR);
    float dist = length(lightDir);
    vec3 halfV = normalize(lightDir + normalize(viewDir));

    float NdotH = dot(normal, halfV);
    float intensity = pow(max(NdotH, 0.0), SPECULAR_SHININESS);

    return intensity * SPECULAR_COLOR * SPECULAR_POWER / dist; 
}

bool pageFlip(vec2 uv, vec2 mouse, vec2 topRight, inout vec2 sampleUV, inout vec3 normal, out float toHalfEdge)
{
    vec2 toEdge = topRight - mouse;
    vec2 toEdgeN = normalize(toEdge);
    
    vec2 toPixel = uv - mouse;
    
    vec2 diagonal = normalize(vec2(1.0));
    float cosT = dot(toEdgeN, diagonal);
    float cosT2 = 2.0 * cosT * cosT - 1.0;
    vec2 toEdgePerp = vec2(toEdgeN.y, -toEdgeN.x);
    
    float dir = sign(dot(toEdgePerp,diagonal));
    float sinT2 = dir * sqrt(1.0 - cosT2 * cosT2);
    
    vec2 nextPageUp = vec2(-sinT2, cosT2);
    vec2 nextPageRight = vec2(nextPageUp.y, -nextPageUp.x);
    
    if(dot(toPixel, nextPageUp) >= 0.0 && dot(toPixel, nextPageRight) >= 0.0)
    {
        vec2 halfPoint = mouse + toEdge * 0.5;
        vec2 toPixHP = uv - halfPoint;
        
        float proj = dot(toEdgePerp, (uv - halfPoint));
        vec2 projPoint = halfPoint + toEdgePerp * proj;
            
        float distDiff = distance(projPoint, uv) / distance(mouse, halfPoint);
        toHalfEdge = distDiff;
        
        if(dot(toPixHP, toEdge) < 0.0)
        {
            sampleUV = (sampleUV - mouse);
            sampleUV = vec2(sampleUV.y, -sampleUV.x);
            mat2 rot = mat2(cosT2, -sinT2, sinT2, cosT2); 
            sampleUV = rot * sampleUV;
            sampleUV.y *= -1.0;
            sampleUV.y = 1.0 - sampleUV.y;
            

            distDiff *= 0.5;
            normal = fakeNormal(distDiff, -0.43);
        }
        else
        {
            
            normal = fakeNormal(uv.x / topRight.x, 0.07);
        }

        return false;
    }
    
    return true;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    vec2 uv = fragCoord/iResolution.xy;

    vec3 bgCol = texture(iChannel0, uv).xyz;

    vec3 outCol = bgCol;
    
    vec2 bookUV = uv - BOOK_BOUNDS.xy;
    vec2 bookSize = vec2(BOOK_BOUNDS.zw - BOOK_BOUNDS.xy);
    bookUV /= bookSize;

    vec2 topRight = vec2(bookSize.y / bookSize.x, 1); 
    vec2 sUV = bookUV;
    sUV.x *= topRight.x;

    vec2 nMouse = iMouse.xy / iResolution.xy;
    nMouse -= BOOK_BOUNDS.xy;
    nMouse /= bookSize.xy;
    nMouse = clamp(nMouse, vec2(0.0),vec2(1.0));

    
    if(iMouse.z <= 0.0 && iTime > 0.0)
    {
        nMouse = vec2((sin(iTime + 0.3) + 1.0) * 0.5,(cos(iTime * 0.5) + 1.0) * 0.5);

    }
        nMouse.x *= topRight.x;


    float radius = topRight.x * 0.5;
    vec2 spineTop = vec2(radius, 1.0);
    vec2 spineMouse = nMouse - spineTop;
    float len = length(spineMouse);
    nMouse = spineTop + normalize(spineMouse) * min(len, radius);

    vec2 sampleUV = sUV;

    vec3 normal = vec3(0,0,1);
    float halfEdgeDist = 1.0;
    bool firstPage = pageFlip(sUV, nMouse, topRight, sampleUV, normal, halfEdgeDist);

    sampleUV.x /= topRight.x;

    vec3 page1 = texture(iChannel2, sampleUV).xyz;
    vec3 page2 = texture(iChannel1, sampleUV).xyz;
    page1 = fakeSpine(page1,bookUV.x, 0.2);
    page2 = fakeSpine(page2,sampleUV.x, 0.2);
    page2 = fakeSpine(page2,  (1.0 -halfEdgeDist) * 0.5, 0.4);

    vec3 pageCol = firstPage ? page1 : page2;

    vec3 normal2 =  fakeNormal(bookUV.x > 0.5 ? bookUV.x : 1.0 - bookUV.x, 0.07);

    normal = firstPage ? normal2 : normal;

    normalize(normal);
    pageCol += min(vec3(1,1,1), specular(vec3(0,0,1), normal)) * 0.3;

    if(sampleUV.x >= 0.0 && sampleUV.x <= 1.0 && sampleUV.y >= 0.0 && sampleUV.y <= 1.0)
    {
        outCol = pageCol;
    }

    fragColor = vec4(outCol,1.0);
}
  • 效果太好的,但也可以学习方法:翻书
    代码:
/* 
	"Flip Page" by Lucian Stanculescu - 2019
	Free to use, credit if you want/can.

	A simple page flip - the page wraps around a rolling cylinder
 
	IMPROVEMENTS
	- normals and lighting
	- compute time when page exits completely
	- use a cone instead of the cylinder with changing radius/height/orientation
*/ 

#define r 0.05
#define rep 1.0

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float R = r * iResolution.x; // radius of rolling cylinder
    
    float v = 1.5 * iResolution.x / rep;
    
    float time = fract(iTime / rep);
    
    vec2 s = fragCoord; // pixel coordinates
    
    vec2 u = normalize(vec2(3.0, 1.0)); // direction of movement
    
    vec2 o = vec2(time *rep* v, 0.0); // origin of cylinder
    
    float d = dot(s - o, u); // distance to generator of cylinder
    
    vec2 h = s - u * d; // projection on generator
    
    bool onCylinder = abs(d) < R;
    
    float angle = onCylinder ? asin(d / R) : 0.0;
    
    bool neg = d < 0.0;
    
    float a0 = 3.141592653 + angle;
    
    float a = onCylinder ? (neg ? -angle : (3.141592653 + angle)) : 0.0; // angle
    
    float l = R * a; // length of arc
    
    vec2 p = h - u * l; // unwrapped point from cylinder to plane
    
    bool outside = any(lessThan(p, vec2(0.0))) || any(greaterThan(p, iResolution.xy));
    
    bool previous = (!onCylinder ||outside) && neg;
    
    vec4 color = (previous ? mix(0.1, 1.0, time): 1.0) * texture(iChannel0, (!onCylinder || outside ? fragCoord : p) / iResolution.xy);
 
    l = R * a0; // length of arc
    
    p = h - u * l; // unwrapped point from cylinder to plane
    
    outside = any(lessThan(p, vec2(0.0))) || any(greaterThan(p, iResolution.xy));
    
    color = outside || !onCylinder ? color : texture(iChannel0, p / iResolution.xy);
    
    // Output to screen
    fragColor = color;
}

额。。搁置了。。滚去复习数学知识了。

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值